diff --git a/.gitignore b/.gitignore index a547bf3..49ef0bd 100644 --- a/.gitignore +++ b/.gitignore @@ -15,6 +15,7 @@ dist-ssr # Editor directories and files .vscode/* !.vscode/extensions.json +!.vscode/settings.json .idea .DS_Store *.suo diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..c91cd66 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,36 @@ +{ + "material-icon-theme.folders.customClones": [ + { + "name": "function-editor", + "base": "functions", + "color": "cyan-800", + "lightColor": "light-green-700", + "folderNames": ["editor"], + "rootFolderNames": ["editor"] + }, + { + "name": "function-graph", + "base": "lottie", + "color": "blue-800", + "lightColor": "light-blue-200", + "folderNames": ["graph"], + "rootFolderNames": ["graph"] + }, + { + "name": "input-component", + "base": "mock", + "color": "cyan-800", + "lightColor": "light-green-700", + "folderNames": ["inputs"], + "rootFolderNames": ["inputs"] + }, + { + "name": "icon", + "base": "svg", + "color": "purple-400", + "lightColor": "purple-100", + "folderNames": ["icons"], + "rootFolderNames": ["icons"] + }, + ] +} diff --git a/package-lock.json b/package-lock.json index 578c5ac..709b998 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,6 +14,8 @@ "function-plot": "^2.0.0-0", "json5": "^2.2.3", "lodash-es": "^4.17.21", + "mitt": "^3.0.1", + "pinia": "^3.0.2", "prettier": "^3.4.2", "sober": "^1.1.0", "utf8": "^3.0.0", @@ -24,6 +26,7 @@ "devDependencies": { "@types/base-64": "^1.0.2", "@types/lodash-es": "^4.17.12", + "@types/node": "^22.14.1", "@types/utf8": "^3.0.3", "@vitejs/plugin-vue": "^5.2.1", "patch-package": "^8.0.0", @@ -879,6 +882,16 @@ "@types/lodash": "*" } }, + "node_modules/@types/node": { + "version": "22.14.1", + "resolved": "https://registry.npmjs.org/@types/node/-/node-22.14.1.tgz", + "integrity": "sha512-u0HuPQwe/dHrItgHHpmw3N2fYCR6x4ivMNbPHRkBVP4CvN+kiRrKHWk3i8tXiO/joPwXLMYvF9TTF0eqgHIuOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~6.21.0" + } + }, "node_modules/@types/sortablejs": { "version": "1.15.8", "resolved": "https://registry.npmjs.org/@types/sortablejs/-/sortablejs-1.15.8.tgz", @@ -1002,6 +1015,30 @@ "integrity": "sha512-sGhTPMuXqZ1rVOk32RylztWkfXTRhuS7vgAKv0zjqk8gbsHkJ7xfFf+jbySxt7tWObEJwyKaHMikV/WGDiQm8g==", "license": "MIT" }, + "node_modules/@vue/devtools-kit": { + "version": "7.7.5", + "resolved": "https://registry.npmjs.org/@vue/devtools-kit/-/devtools-kit-7.7.5.tgz", + "integrity": "sha512-S9VAVJYVAe4RPx2JZb9ZTEi0lqTySz2CBeF0wHT5D3dkTLnT9yMMGegKNl4b2EIELwLSkcI9bl2qp0/jW+upqA==", + "license": "MIT", + "dependencies": { + "@vue/devtools-shared": "^7.7.5", + "birpc": "^2.3.0", + "hookable": "^5.5.3", + "mitt": "^3.0.1", + "perfect-debounce": "^1.0.0", + "speakingurl": "^14.0.1", + "superjson": "^2.2.2" + } + }, + "node_modules/@vue/devtools-shared": { + "version": "7.7.5", + "resolved": "https://registry.npmjs.org/@vue/devtools-shared/-/devtools-shared-7.7.5.tgz", + "integrity": "sha512-QBjG72RfpM0DKtpns2RZOxBltO226kOAls9e4Lri6YxS2gWTgL0H+wj1R2K76lxxIeOrqo4+2Ty6RQnzv+WSTQ==", + "license": "MIT", + "dependencies": { + "rfdc": "^1.4.1" + } + }, "node_modules/@vue/language-core": { "version": "2.2.8", "resolved": "https://registry.npmjs.org/@vue/language-core/-/language-core-2.2.8.tgz", @@ -1156,6 +1193,15 @@ "integrity": "sha512-kwDPIFCGx0NZHog36dj+tHiwP4QMzsZ3AgMViUBKI0+V5n4U0ufTCUMhnQ04diaRI8EX/QcPfql7zlhZ7j4zgg==", "license": "MIT" }, + "node_modules/birpc": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/birpc/-/birpc-2.3.0.tgz", + "integrity": "sha512-ijbtkn/F3Pvzb6jHypHRyve2QApOCZDR25D/VnkY2G/lBNcXCTsnsCxgY4k4PkVB7zfwzYbY3O9Lcqe3xufS5g==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/antfu" + } + }, "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", @@ -1299,6 +1345,21 @@ "dev": true, "license": "MIT" }, + "node_modules/copy-anything": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/copy-anything/-/copy-anything-3.0.5.tgz", + "integrity": "sha512-yCEafptTtb4bk7GLEQoM8KVJpxAfdBJYaXyzQEgQQQgYrZiDp8SJmGKlYza6CYjEDNstAdNdKA3UuoULlEbS6w==", + "license": "MIT", + "dependencies": { + "is-what": "^4.1.8" + }, + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -1903,6 +1964,12 @@ "he": "bin/he" } }, + "node_modules/hookable": { + "version": "5.5.3", + "resolved": "https://registry.npmjs.org/hookable/-/hookable-5.5.3.tgz", + "integrity": "sha512-Yc+BQe8SvoXH1643Qez1zqLRmbA5rCL+sSmk6TVos0LWVfNIB7PGncdlId77WzLGSIB5KaWgTaNTs2lNVEI6VQ==", + "license": "MIT" + }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", @@ -1986,6 +2053,18 @@ "node": ">=0.12.0" } }, + "node_modules/is-what": { + "version": "4.1.16", + "resolved": "https://registry.npmjs.org/is-what/-/is-what-4.1.16.tgz", + "integrity": "sha512-ZhMwEosbFJkA0YhFnNDgTM4ZxDRsS6HqTo7qsZM08fehyRYIYa0yHu5R6mgo1n/8MgaPBXiPimPD77baVFYg+A==", + "license": "MIT", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/is-wsl": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", @@ -2159,6 +2238,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/mitt": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mitt/-/mitt-3.0.1.tgz", + "integrity": "sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==", + "license": "MIT" + }, "node_modules/mr-parser": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/mr-parser/-/mr-parser-0.2.1.tgz", @@ -2304,6 +2389,12 @@ "node": ">=8" } }, + "node_modules/perfect-debounce": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/perfect-debounce/-/perfect-debounce-1.0.0.tgz", + "integrity": "sha512-xCy9V055GLEqoFaHoC1SoLIaLmWctgCUaBaWxDZ7/Zx4CTyX7cJQLJOok/orfjZAh9kEYpjJa4d0KcJmCbctZA==", + "license": "MIT" + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -2323,6 +2414,36 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/pinia": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pinia/-/pinia-3.0.2.tgz", + "integrity": "sha512-sH2JK3wNY809JOeiiURUR0wehJ9/gd9qFN2Y828jCbxEzKEmEt0pzCXwqiSTfuRsK9vQsOflSdnbdBOGrhtn+g==", + "license": "MIT", + "dependencies": { + "@vue/devtools-api": "^7.7.2" + }, + "funding": { + "url": "https://github.com/sponsors/posva" + }, + "peerDependencies": { + "typescript": ">=4.4.4", + "vue": "^2.7.0 || ^3.5.11" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/pinia/node_modules/@vue/devtools-api": { + "version": "7.7.5", + "resolved": "https://registry.npmjs.org/@vue/devtools-api/-/devtools-api-7.7.5.tgz", + "integrity": "sha512-HYV3tJGARROq5nlVMJh5KKHk7GU8Au3IrrmNNqr978m0edxgpHgYPDoNUGrvEgIbObz09SQezFR3A1EVmB5WZg==", + "license": "MIT", + "dependencies": { + "@vue/devtools-kit": "^7.7.5" + } + }, "node_modules/postcss": { "version": "8.5.3", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", @@ -2366,6 +2487,12 @@ "url": "https://github.com/prettier/prettier?sponsor=1" } }, + "node_modules/rfdc": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.4.1.tgz", + "integrity": "sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==", + "license": "MIT" + }, "node_modules/rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -2499,6 +2626,27 @@ "node": ">=0.10.0" } }, + "node_modules/speakingurl": { + "version": "14.0.1", + "resolved": "https://registry.npmjs.org/speakingurl/-/speakingurl-14.0.1.tgz", + "integrity": "sha512-1POYv7uv2gXoyGFpBCmpDVSNV74IfsWlDW216UPjbWufNf+bSU6GdbDsxdcxtfwb4xlI3yxzOTKClUosxARYrQ==", + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/superjson": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/superjson/-/superjson-2.2.2.tgz", + "integrity": "sha512-5JRxVqC8I8NuOUjzBbvVJAKNM8qoVuH0O77h4WInc/qC2q5IreqKxYwgkga3PfA22OayK2ikceb/B26dztPl+Q==", + "license": "MIT", + "dependencies": { + "copy-anything": "^3.0.2" + }, + "engines": { + "node": ">=16" + } + }, "node_modules/supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", @@ -2597,6 +2745,13 @@ "node": ">=14.17" } }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" + }, "node_modules/universalify": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", diff --git a/package.json b/package.json index 739ac5c..b6bd298 100644 --- a/package.json +++ b/package.json @@ -15,6 +15,8 @@ "function-plot": "^2.0.0-0", "json5": "^2.2.3", "lodash-es": "^4.17.21", + "mitt": "^3.0.1", + "pinia": "^3.0.2", "prettier": "^3.4.2", "sober": "^1.1.0", "utf8": "^3.0.0", @@ -25,6 +27,7 @@ "devDependencies": { "@types/base-64": "^1.0.2", "@types/lodash-es": "^4.17.12", + "@types/node": "^22.14.1", "@types/utf8": "^3.0.3", "@vitejs/plugin-vue": "^5.2.1", "patch-package": "^8.0.0", diff --git a/src/App.vue b/src/App.vue index 4737101..8b7740d 100644 --- a/src/App.vue +++ b/src/App.vue @@ -5,191 +5,53 @@ -
-
- - - - - -
- - {{ t("buttons.add") }} - -
-
- - - - - - - - - - {{ t("buttons.import") }} - -
{{ t("title.source") }}
- - - {{ t("buttons.cancel") }} - - - {{ t("buttons.confirm") }} - -
-
- -
-
-
- +
+ +
+ diff --git a/src/components/graph.vue b/src/graph/graph.vue similarity index 73% rename from src/components/graph.vue rename to src/graph/graph.vue index 2f08112..3efe914 100644 --- a/src/components/graph.vue +++ b/src/graph/graph.vue @@ -2,26 +2,17 @@
- - - - - - + + {{ t("buttons.reset") }} - - - - - - + +
{{ errorMsg }}
@@ -32,6 +23,9 @@ import { useI18n } from "vue-i18n"; const { t } = useI18n(); +import SIconRefresh from "@/ui/icons/refresh.vue"; +import SIconWarn from "@/ui/icons/warn.vue"; + import { onMounted, ref, watch } from "vue"; import { cloneDeep, throttle } from "lodash-es"; import type { FunctionPlotDatum } from "function-plot"; @@ -41,7 +35,8 @@ const { data, width, height } = defineProps<{ width: number; height: number; }>(); -const emit = defineEmits(["requireFullUpdate", "requirePostUpdate"]); + +import emitter from "@/mitt"; const fullUpdateState = defineModel(); const plotRef = ref(null); @@ -81,11 +76,11 @@ onMounted(async () => { }); if (fullUpdateState.value) { fullUpdateState.value = false; - emit("requirePostUpdate"); + emitter.emit("require-post-update"); } else errorMsg.value = undefined; } catch (e) { // console.log(e); - if (!fullUpdateState.value) emit("requireFullUpdate"); + if (!fullUpdateState.value) emitter.emit("require-full-update"); errorMsg.value = (e as Error).message; } }, 200), @@ -130,10 +125,6 @@ onMounted(async () => { flex-direction: column; } -#warningIcon { - color: var(--s-color-warning); -} - .tooltipMsgPre { margin: 0; padding: 0; diff --git a/src/graph/index.vue b/src/graph/index.vue new file mode 100644 index 0000000..740a5db --- /dev/null +++ b/src/graph/index.vue @@ -0,0 +1,51 @@ + + + diff --git a/src/i18n.ts b/src/i18n.ts index 0a9cc43..8cd1ea0 100644 --- a/src/i18n.ts +++ b/src/i18n.ts @@ -1,5 +1,7 @@ -import { createI18n } from "vue-i18n"; -export default createI18n({ +import type { createI18n } from "vue-i18n"; +type I18nOpt = Parameters[0]; + +export default { legacy: false, locale: "zh-CN", messages: { @@ -106,4 +108,4 @@ export default createI18n({ }, }, }, -}); +} as I18nOpt; diff --git a/src/main.ts b/src/main.ts index 707c88f..a96818f 100644 --- a/src/main.ts +++ b/src/main.ts @@ -1,12 +1,17 @@ -import { createApp } from "vue"; - -import i18n from "./i18n"; -import "./style.css"; -import App from "./App.vue"; - import "sober"; +import "./public.css"; +import { createApp } from "vue"; +import App from "./App.vue"; const app = createApp(App); +import { createPinia } from 'pinia' +const pinia = createPinia() +app.use(pinia); + +import { createI18n } from "vue-i18n"; +import i18nData from "./i18n"; +const i18n = createI18n(i18nData); app.use(i18n); + app.mount("#app"); diff --git a/src/mitt.ts b/src/mitt.ts new file mode 100644 index 0000000..62613ca --- /dev/null +++ b/src/mitt.ts @@ -0,0 +1,8 @@ +import mitt from "mitt"; + +const emitter = mitt(); +type Events = { + "require-full-update": void; + "require-post-update": void; +}; +export default emitter; diff --git a/src/public.css b/src/public.css index e69de29..1dcf8eb 100644 --- a/src/public.css +++ b/src/public.css @@ -0,0 +1,46 @@ +:root { + color-scheme: dark; + line-height: 1.5; + font-weight: 400; + font-synthesis: none; + text-rendering: optimizeLegibility; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + cursor: default; + user-select: none; +} + +:root, +input, +select, +button, +textarea { + font-family: "HarmonyOS Sans SC", sans-serif; +} + +pre, +input[type="text"], +input[type="number"] { + font-family: "Jetbrains Mono", monospace; +} + +* { + outline: none; +} + +@font-face { + font-family: "KaTeX"; + src: url("/fonts/KaTeX_AllInOne.woff2"); +} + +.styled::part(input), +.styled { + font-family: "KaTeX", "Times new roman"; + font-variant-ligatures: common-ligatures discretionary-ligatures contextual + historical-ligatures; +} + +.monospace, +.monospace::part(input) { + font-family: "Jetbrains Mono", monospace; +} diff --git a/src/style.css b/src/style.css deleted file mode 100644 index 1dcf8eb..0000000 --- a/src/style.css +++ /dev/null @@ -1,46 +0,0 @@ -:root { - color-scheme: dark; - line-height: 1.5; - font-weight: 400; - font-synthesis: none; - text-rendering: optimizeLegibility; - -webkit-font-smoothing: antialiased; - -moz-osx-font-smoothing: grayscale; - cursor: default; - user-select: none; -} - -:root, -input, -select, -button, -textarea { - font-family: "HarmonyOS Sans SC", sans-serif; -} - -pre, -input[type="text"], -input[type="number"] { - font-family: "Jetbrains Mono", monospace; -} - -* { - outline: none; -} - -@font-face { - font-family: "KaTeX"; - src: url("/fonts/KaTeX_AllInOne.woff2"); -} - -.styled::part(input), -.styled { - font-family: "KaTeX", "Times new roman"; - font-variant-ligatures: common-ligatures discretionary-ligatures contextual - historical-ligatures; -} - -.monospace, -.monospace::part(input) { - font-family: "Jetbrains Mono", monospace; -} diff --git a/src/types.ts b/src/types.ts index e69de29..986b390 100644 --- a/src/types.ts +++ b/src/types.ts @@ -0,0 +1,9 @@ + +type NormalizeProp = + (T extends object ? true : false) extends false ? T : // 全原始类型 + T extends object ? NormalizeObj : never // 含对象则归一为对象 + +type NormalizeObj = { + [K in keyof T]-?: NormalizeProp +} + diff --git a/src/ui/icons/delete.vue b/src/ui/icons/delete.vue new file mode 100644 index 0000000..712e323 --- /dev/null +++ b/src/ui/icons/delete.vue @@ -0,0 +1,9 @@ + diff --git a/src/ui/icons/drag.vue b/src/ui/icons/drag.vue new file mode 100644 index 0000000..583f717 --- /dev/null +++ b/src/ui/icons/drag.vue @@ -0,0 +1,9 @@ + diff --git a/src/ui/icons/github.vue b/src/ui/icons/github.vue new file mode 100644 index 0000000..74a41df --- /dev/null +++ b/src/ui/icons/github.vue @@ -0,0 +1,10 @@ + diff --git a/src/ui/icons/hide.vue b/src/ui/icons/hide.vue new file mode 100644 index 0000000..8775518 --- /dev/null +++ b/src/ui/icons/hide.vue @@ -0,0 +1,9 @@ + diff --git a/src/ui/icons/import.vue b/src/ui/icons/import.vue new file mode 100644 index 0000000..2713f3a --- /dev/null +++ b/src/ui/icons/import.vue @@ -0,0 +1,9 @@ + diff --git a/src/ui/icons/language.vue b/src/ui/icons/language.vue new file mode 100644 index 0000000..647037a --- /dev/null +++ b/src/ui/icons/language.vue @@ -0,0 +1,9 @@ + diff --git a/src/ui/icons/refresh.vue b/src/ui/icons/refresh.vue new file mode 100644 index 0000000..7ce5508 --- /dev/null +++ b/src/ui/icons/refresh.vue @@ -0,0 +1,9 @@ + diff --git a/src/ui/icons/warn.vue b/src/ui/icons/warn.vue new file mode 100644 index 0000000..d2b3fcd --- /dev/null +++ b/src/ui/icons/warn.vue @@ -0,0 +1,9 @@ + diff --git a/src/components/nav.vue b/src/ui/navbar.vue similarity index 51% rename from src/components/nav.vue rename to src/ui/navbar.vue index a9015f5..f445e4d 100644 --- a/src/components/nav.vue +++ b/src/ui/navbar.vue @@ -16,27 +16,14 @@ :label="t('inputs.language')" > - - - - - + 简体中文 English - - - - - + {{ t("buttons.repo") }} @@ -49,6 +36,9 @@ import { useI18n } from "vue-i18n"; const { locale, t } = useI18n(); +import SIconLanguage from "@/ui/icons/language.vue"; +import SIconGtihub from "@/ui/icons/github.vue"; + const lang = ref(""); watchEffect(() => console.log(lang.value)); diff --git a/tsconfig.app.json b/tsconfig.app.json index 0a1fffd..b95841b 100644 --- a/tsconfig.app.json +++ b/tsconfig.app.json @@ -20,7 +20,12 @@ "noUnusedLocals": true, "noUnusedParameters": true, "noFallthroughCasesInSwitch": true, - "noUncheckedSideEffectImports": true + "noUncheckedSideEffectImports": true, + + "baseUrl": ".", + "paths": { + "@/*": ["src/*"] + } }, "include": ["src/**/*.ts", "src/**/*.tsx", "src/**/*.vue"] } diff --git a/vite.config.ts b/vite.config.ts index fbfdf71..12e8fc5 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,5 +1,6 @@ import { defineConfig } from "vite"; import vue from "@vitejs/plugin-vue"; +import { resolve } from "node:path"; // https://vite.dev/config/ export default defineConfig({ @@ -13,4 +14,12 @@ export default defineConfig({ }), ], build: { chunkSizeWarningLimit: 1024 }, + resolve: { + alias: [ + { + find: "@", + replacement: resolve(__dirname, "./src"), + }, + ], + }, });