feat(frontend): Vue 3 foundation — Tailwind 3, Vite alias, shared types

Migrates n-link-core's Tailwind config from 1.x to 3.x (drops future/
variants/purge, swaps custom-forms for @tailwindcss/forms) and replaces
the Vue 2 'vue/types/vue' augmentation with Vue 3
ComponentCustomProperties. Deletes the Vue 2 shims.
This commit is contained in:
Your Name 2026-07-21 20:44:28 -04:00
parent 15f219bd43
commit 49e9e3879e
10 changed files with 1090 additions and 415 deletions

View file

@ -13,11 +13,20 @@
"@tauri-apps/api": "^2.11.1",
"@tauri-apps/plugin-dialog": "^2.7.2",
"@tauri-apps/plugin-shell": "^2.3.5",
"vue": "^3.5.13"
"element-plus": "^2.14.3",
"feather-icons": "^4.29.2",
"filesize": "^10.1.6",
"vue": "^3.5.13",
"vue-router": "^4.6.4"
},
"devDependencies": {
"@tailwindcss/forms": "^0.5.11",
"@tauri-apps/cli": "^2",
"@vitejs/plugin-vue": "^5.2.1",
"autoprefixer": "^10.5.4",
"postcss": "^8.5.21",
"sass": "^1.101.3",
"tailwindcss": "^3.4.19",
"typescript": "~5.6.2",
"vite": "^6.0.3",
"vue-tsc": "^2.1.10"

View file

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
};

View file

@ -0,0 +1,8 @@
module.exports = {
content: [
'./index.html',
'./src/**/*.{vue,js,ts}',
'../n-link-core/components/**/*.{vue,js,ts}',
],
presets: [require('../n-link-core/tailwind.config.js')],
};

View file

@ -1,32 +1,24 @@
import { defineConfig } from "vite";
import vue from "@vitejs/plugin-vue";
import { fileURLToPath, URL } from "node:url";
// @ts-expect-error process is a nodejs global
const host = process.env.TAURI_DEV_HOST;
// https://vite.dev/config/
export default defineConfig(async () => ({
plugins: [vue()],
// Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build`
//
// 1. prevent Vite from obscuring rust errors
resolve: {
alias: {
"n-link-core": fileURLToPath(new URL("../n-link-core", import.meta.url)),
"@": fileURLToPath(new URL("./src", import.meta.url)),
},
},
// Vite ignores watching src-tauri; Tauri expects a fixed port.
clearScreen: false,
// 2. tauri expects a fixed port, fail if that port is not available
server: {
port: 1420,
strictPort: true,
host: host || false,
hmr: host
? {
protocol: "ws",
host,
port: 1421,
}
: undefined,
watch: {
// 3. tell Vite to ignore watching `src-tauri`
ignored: ["**/src-tauri/**"],
},
hmr: host ? { protocol: "ws", host, port: 1421 } : undefined,
watch: { ignored: ["**/src-tauri/**"] },
},
}));

View file

@ -59,9 +59,20 @@ export interface GenericDevices {
move(dev: DevId | string, src: string, dest: string): Promise<void>;
}
declare module 'vue/types/vue' {
// 3. Declare augmentation for Vue
interface Vue {
import type { InjectionKey } from 'vue';
/**
* n-link-core is consumed by more than one host (the Tauri desktop app and
* the WebUSB web app), each of which supplies its own GenericDevices
* implementation. Core components must therefore never import a concrete
* store they inject this key. The host provides it at startup.
*/
export const DEVICES_KEY = Symbol('devices') as InjectionKey<GenericDevices>;
declare module 'vue' {
interface ComponentCustomProperties {
$devices: GenericDevices;
}
}
export {};

View file

@ -1,18 +1,12 @@
{
"name": "n-link-core",
"version": "0.0.0",
"dependencies": {
"element-ui": "^2.13.2",
"vue": "^2.6.11",
"vue-class-component": "^7.2.3",
"vue-property-decorator": "^9.0.2"
"peerDependencies": {
"element-plus": "^2.14.3",
"vue": "^3.5.40"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^4.4.0",
"@typescript-eslint/parser": "^4.4.0",
"@vue/eslint-config-typescript": "^5.1.0",
"eslint": "^6.7.2",
"eslint-plugin-vue": "^6.2.2",
"tailwindcss": "^1.8.9"
"tailwindcss": "^3.4.19",
"@tailwindcss/forms": "^0.5.11"
}
}

View file

@ -1,13 +0,0 @@
import Vue, { VNode } from 'vue'
declare global {
namespace JSX {
// tslint:disable no-empty-interface
interface Element extends VNode {}
// tslint:disable no-empty-interface
interface ElementClass extends Vue {}
interface IntrinsicElements {
[elem: string]: any;
}
}
}

View file

@ -1,54 +0,0 @@
declare module '*.vue' {
import Vue from 'vue'
export default Vue
}
import Vue, { PluginFunction } from 'vue';
interface IAsyncComputedOptions {
errorHandler?: (error: string[]) => void;
useRawError?: boolean;
default?: any;
}
export default class AsyncComputed {
constructor(options?: IAsyncComputedOptions);
static install: PluginFunction<never>;
static version: string;
}
type AsyncComputedGetter<T> = () => Promise<T> | T;
export interface IAsyncComputedProperty<T> {
default?: T | (() => T);
get: AsyncComputedGetter<T>;
watch?: () => void;
shouldUpdate?: () => boolean;
lazy?: boolean;
}
interface IAsyncComputedProperties {
[K: string]: AsyncComputedGetter<any> | IAsyncComputedProperty<any>;
}
declare module 'vue/types/options' {
interface ComponentOptions<V extends Vue> {
// @ts-ignore
asyncComputed?: IAsyncComputedProperties;
}
}
interface IASyncComputedState {
state: 'updating' | 'success' | 'error';
updating: boolean;
success: boolean;
error: boolean;
exception: Error | null;
update: () => void;
}
declare module 'vue/types/vue' {
// tslint:disable-next-line:interface-name
interface Vue {
$asyncComputed: { [K: string]: IASyncComputedState };
}
}

View file

@ -1,8 +1,4 @@
module.exports = {
future: {
purgeLayersByDefault: true,
removeDeprecatedGapUtilities: true,
},
theme: {
fontFamily: {
sans: [
@ -51,17 +47,5 @@ module.exports = {
},
},
},
variants: {},
plugins: [require('@tailwindcss/custom-forms')],
purge: process.env.npm_package_name === 'web' && {
enabled: process.env.NODE_ENV === 'production',
content: [
'components/**/*.vue',
'layouts/**/*.vue',
'pages/**/*.vue',
'plugins/**/*.js',
'nuxt.config.js',
'../n-link-core/components/**/*.vue'
]
},
plugins: [require('@tailwindcss/forms')],
};

1334
package-lock.json generated

File diff suppressed because it is too large Load diff