feat(webui): add TypeScript types and ESLint Svelte plugin
- commit
- f328a4f28a8984d44d992817dc6b46b9c21fcb1c
- author
- brice · · 2026-01-24 14:19 UTC
- parent
- 11eef0c
- signature
- GPG Verified
CD88F9E564E0102646FB6077A1A07C7C7C3E349D· arch-gpg Verified for the committer email
6 files · +518 -35
M webui/eslint.config.js +21 -0
| @@ -1,9 +1,30 @@ | |||
| 1 | 1 | import js from "@eslint/js"; | |
| 2 | + | import svelte from "eslint-plugin-svelte"; | |
| 3 | + | import globals from "globals"; | |
| 2 | 4 | import tseslint from "typescript-eslint"; | |
| 5 | + | import svelteConfig from "./svelte.config.js"; | |
| 3 | 6 | ||
| 4 | 7 | export default tseslint.config( | |
| 5 | 8 | js.configs.recommended, | |
| 6 | 9 | ...tseslint.configs.recommended, | |
| 10 | + | ...svelte.configs.recommended, | |
| 11 | + | { | |
| 12 | + | languageOptions: { | |
| 13 | + | globals: { | |
| 14 | + | ...globals.browser, | |
| 15 | + | }, | |
| 16 | + | }, | |
| 17 | + | }, | |
| 18 | + | { | |
| 19 | + | files: ["**/*.svelte", "**/*.svelte.ts", "**/*.svelte.js"], | |
| 20 | + | languageOptions: { | |
| 21 | + | parserOptions: { | |
| 22 | + | extraFileExtensions: [".svelte"], | |
| 23 | + | parser: tseslint.parser, | |
| 24 | + | svelteConfig, | |
| 25 | + | }, | |
| 26 | + | }, | |
| 27 | + | }, | |
| 7 | 28 | { | |
| 8 | 29 | files: ["**/*.{js,mjs,cjs,ts}"], | |
| 9 | 30 | languageOptions: { | |
| @@ -1,9 +1,30 @@ | |||||
| 1 | import js from "@eslint/js"; | 1 | import js from "@eslint/js"; | ||
| 2 | + | import svelte from "eslint-plugin-svelte"; | |||
| 3 | + | import globals from "globals"; | |||
| 2 | import tseslint from "typescript-eslint"; | 4 | import tseslint from "typescript-eslint"; | ||
| 5 | + | import svelteConfig from "./svelte.config.js"; | |||
| 3 | 6 | ||||
| 4 | export default tseslint.config( | 7 | export default tseslint.config( | ||
| 5 | js.configs.recommended, | 8 | js.configs.recommended, | ||
| 6 | ...tseslint.configs.recommended, | 9 | ...tseslint.configs.recommended, | ||
| 10 | + | ...svelte.configs.recommended, | |||
| 11 | + | { | |||
| 12 | + | languageOptions: { | |||
| 13 | + | globals: { | |||
| 14 | + | ...globals.browser, | |||
| 15 | + | }, | |||
| 16 | + | }, | |||
| 17 | + | }, | |||
| 18 | + | { | |||
| 19 | + | files: ["**/*.svelte", "**/*.svelte.ts", "**/*.svelte.js"], | |||
| 20 | + | languageOptions: { | |||
| 21 | + | parserOptions: { | |||
| 22 | + | extraFileExtensions: [".svelte"], | |||
| 23 | + | parser: tseslint.parser, | |||
| 24 | + | svelteConfig, | |||
| 25 | + | }, | |||
| 26 | + | }, | |||
| 27 | + | }, | |||
| 7 | { | 28 | { | ||
| 8 | files: ["**/*.{js,mjs,cjs,ts}"], | 29 | files: ["**/*.{js,mjs,cjs,ts}"], | ||
| 9 | languageOptions: { | 30 | languageOptions: { | ||
M webui/package-lock.json +308 -7 generated file: collapsed
M webui/package.json +2 -0
| @@ -27,6 +27,8 @@ | |||
| 27 | 27 | "@typescript-eslint/parser": "^7.0.0", | |
| 28 | 28 | "autoprefixer": "^10.4.20", | |
| 29 | 29 | "eslint": "^8.57.0", | |
| 30 | + | "eslint-plugin-svelte": "^3.14.0", | |
| 31 | + | "globals": "^17.1.0", | |
| 30 | 32 | "postcss": "^8.4.47", | |
| 31 | 33 | "svelte": "^5.0.0", | |
| 32 | 34 | "svelte-awesome-color-picker": "^4.1.0", | |
| @@ -27,6 +27,8 @@ | |||||
| 27 | "@typescript-eslint/parser": "^7.0.0", | 27 | "@typescript-eslint/parser": "^7.0.0", | ||
| 28 | "autoprefixer": "^10.4.20", | 28 | "autoprefixer": "^10.4.20", | ||
| 29 | "eslint": "^8.57.0", | 29 | "eslint": "^8.57.0", | ||
| 30 | + | "eslint-plugin-svelte": "^3.14.0", | |||
| 31 | + | "globals": "^17.1.0", | |||
| 30 | "postcss": "^8.4.47", | 32 | "postcss": "^8.4.47", | ||
| 31 | "svelte": "^5.0.0", | 33 | "svelte": "^5.0.0", | ||
| 32 | "svelte-awesome-color-picker": "^4.1.0", | 34 | "svelte-awesome-color-picker": "^4.1.0", | ||
M webui/src/pages/homepage/messaging.ts +34 -27
| @@ -23,8 +23,12 @@ import { homepageState } from './state.svelte'; | |||
| 23 | 23 | // REQUEST TRACKING | |
| 24 | 24 | // ═══════════════════════════════════════════════════════════════════════════════ | |
| 25 | 25 | ||
| 26 | - | interface PendingRequest<T = unknown> { | |
| 27 | - | resolve: (value: T) => void; | |
| 26 | + | // Pending requests store resolvers for different response types. | |
| 27 | + | // The resolve function accepts `unknown` since requests with different | |
| 28 | + | // response types share the same map. Type safety is maintained at the | |
| 29 | + | // sendMessage<T> API level. | |
| 30 | + | interface PendingRequest { | |
| 31 | + | resolve: (value: unknown) => void; | |
| 28 | 32 | reject: (error: Error) => void; | |
| 29 | 33 | timeout: ReturnType<typeof setTimeout>; | |
| 30 | 34 | } | |
| @@ -38,8 +42,8 @@ let callbacksInitialized = false; | |||
| 38 | 42 | // WEBKIT BRIDGE UTILITIES | |
| 39 | 43 | // ═══════════════════════════════════════════════════════════════════════════════ | |
| 40 | 44 | ||
| 41 | - | function getWebKitBridge(): { postMessage: (msg: unknown) => void } | null { | |
| 42 | - | const bridge = (window as any).webkit?.messageHandlers?.dumber; | |
| 45 | + | function getWebKitBridge(): DumberBridge | null { | |
| 46 | + | const bridge = window.webkit?.messageHandlers?.dumber; | |
| 43 | 47 | if (bridge && typeof bridge.postMessage === 'function') { | |
| 44 | 48 | return bridge; | |
| 45 | 49 | } | |
| @@ -47,7 +51,7 @@ function getWebKitBridge(): { postMessage: (msg: unknown) => void } | null { | |||
| 47 | 51 | } | |
| 48 | 52 | ||
| 49 | 53 | function getWebViewId(): number { | |
| 50 | - | return (window as any).__dumber_webview_id || 0; | |
| 54 | + | return window.__dumber_webview_id ?? 0; | |
| 51 | 55 | } | |
| 52 | 56 | ||
| 53 | 57 | function generateRequestId(type: string): string { | |
| @@ -62,9 +66,7 @@ function initializeCallbacks(): void { | |||
| 62 | 66 | if (callbacksInitialized) return; | |
| 63 | 67 | ||
| 64 | 68 | // Generic response handler - accepts a single Response object from Go backend | |
| 65 | - | (window as any).__dumber_homepage_response = ( | |
| 66 | - | response: { requestId: string; success: boolean; data?: unknown; error?: string } | |
| 67 | - | ) => { | |
| 69 | + | window.__dumber_homepage_response = (response) => { | |
| 68 | 70 | const request = pendingRequests.get(response.requestId); | |
| 69 | 71 | if (!request) return; | |
| 70 | 72 | ||
| @@ -74,61 +76,60 @@ function initializeCallbacks(): void { | |||
| 74 | 76 | if (response.success) { | |
| 75 | 77 | request.resolve(response.data); | |
| 76 | 78 | } else { | |
| 77 | - | request.reject(new Error(response.error || 'Unknown error')); | |
| 79 | + | request.reject(new Error(response.error ?? 'Unknown error')); | |
| 78 | 80 | } | |
| 79 | 81 | }; | |
| 80 | 82 | ||
| 81 | 83 | // Legacy callbacks for backward compatibility | |
| 82 | 84 | // These can receive either raw data or a Response object | |
| 83 | - | (window as any).__dumber_history_timeline = (dataOrResponse: unknown) => { | |
| 85 | + | window.__dumber_history_timeline = (dataOrResponse) => { | |
| 84 | 86 | handleLegacyOrResponseCallback(dataOrResponse); | |
| 85 | 87 | }; | |
| 86 | 88 | ||
| 87 | - | (window as any).__dumber_folders = (dataOrResponse: unknown) => { | |
| 89 | + | window.__dumber_folders = (dataOrResponse) => { | |
| 88 | 90 | handleLegacyOrResponseCallback(dataOrResponse); | |
| 89 | 91 | }; | |
| 90 | 92 | ||
| 91 | - | (window as any).__dumber_tags = (dataOrResponse: unknown) => { | |
| 93 | + | window.__dumber_tags = (dataOrResponse) => { | |
| 92 | 94 | handleLegacyOrResponseCallback(dataOrResponse); | |
| 93 | 95 | }; | |
| 94 | 96 | ||
| 95 | - | (window as any).__dumber_favorites = (dataOrResponse: unknown) => { | |
| 97 | + | window.__dumber_favorites = (dataOrResponse) => { | |
| 96 | 98 | handleLegacyOrResponseCallback(dataOrResponse); | |
| 97 | 99 | }; | |
| 98 | 100 | ||
| 99 | - | (window as any).__dumber_analytics = (dataOrResponse: unknown) => { | |
| 101 | + | window.__dumber_analytics = (dataOrResponse) => { | |
| 100 | 102 | handleLegacyOrResponseCallback(dataOrResponse); | |
| 101 | 103 | }; | |
| 102 | 104 | ||
| 103 | - | (window as any).__dumber_domain_stats = (dataOrResponse: unknown) => { | |
| 105 | + | window.__dumber_domain_stats = (dataOrResponse) => { | |
| 104 | 106 | handleLegacyOrResponseCallback(dataOrResponse); | |
| 105 | 107 | }; | |
| 106 | 108 | ||
| 107 | - | (window as any).__dumber_history_search_results = (dataOrResponse: unknown) => { | |
| 109 | + | window.__dumber_history_search_results = (dataOrResponse) => { | |
| 108 | 110 | handleLegacyOrResponseCallback(dataOrResponse); | |
| 109 | 111 | }; | |
| 110 | 112 | ||
| 111 | - | (window as any).__dumber_history_deleted = (dataOrResponse: unknown) => { | |
| 113 | + | window.__dumber_history_deleted = (dataOrResponse) => { | |
| 112 | 114 | handleLegacyOrResponseCallback(dataOrResponse); | |
| 113 | 115 | }; | |
| 114 | 116 | ||
| 115 | - | (window as any).__dumber_history_cleared = (dataOrResponse: unknown) => { | |
| 117 | + | window.__dumber_history_cleared = (dataOrResponse) => { | |
| 116 | 118 | handleLegacyOrResponseCallback(dataOrResponse); | |
| 117 | 119 | }; | |
| 118 | 120 | ||
| 119 | - | (window as any).__dumber_error = (errorOrResponse: unknown) => { | |
| 121 | + | window.__dumber_error = (errorOrResponse) => { | |
| 120 | 122 | // Handle both formats: direct error string or Response object with error | |
| 121 | - | const response = errorOrResponse as { requestId?: string; error?: string } | string; | |
| 122 | - | if (typeof response === 'string') { | |
| 123 | + | if (typeof errorOrResponse === 'string') { | |
| 123 | 124 | // Legacy format: direct error string | |
| 124 | - | console.error('[messaging] Legacy error:', response); | |
| 125 | - | } else if (response && typeof response === 'object') { | |
| 125 | + | console.error('[messaging] Legacy error:', errorOrResponse); | |
| 126 | + | } else if (errorOrResponse && typeof errorOrResponse === 'object') { | |
| 126 | 127 | // Response object format | |
| 127 | - | const request = pendingRequests.get(response.requestId || 'default'); | |
| 128 | + | const request = pendingRequests.get(errorOrResponse.requestId ?? 'default'); | |
| 128 | 129 | if (request) { | |
| 129 | 130 | clearTimeout(request.timeout); | |
| 130 | - | pendingRequests.delete(response.requestId || 'default'); | |
| 131 | - | request.reject(new Error(response.error || 'Unknown error')); | |
| 131 | + | pendingRequests.delete(errorOrResponse.requestId ?? 'default'); | |
| 132 | + | request.reject(new Error(errorOrResponse.error ?? 'Unknown error')); | |
| 132 | 133 | } | |
| 133 | 134 | } | |
| 134 | 135 | }; | |
| @@ -188,7 +189,13 @@ async function sendMessage<T = unknown>( | |||
| 188 | 189 | reject(new Error(`Request timeout: ${type}`)); | |
| 189 | 190 | }, REQUEST_TIMEOUT_MS); | |
| 190 | 191 | ||
| 191 | - | pendingRequests.set(requestId, { resolve: resolve as any, reject, timeout }); | |
| 192 | + | // Cast resolve to accept unknown - type safety is maintained at API level | |
| 193 | + | // since sendMessage<T> returns Promise<T> and callers expect typed responses | |
| 194 | + | pendingRequests.set(requestId, { | |
| 195 | + | resolve: resolve as (value: unknown) => void, | |
| 196 | + | reject, | |
| 197 | + | timeout, | |
| 198 | + | }); | |
| 192 | 199 | ||
| 193 | 200 | try { | |
| 194 | 201 | bridge.postMessage({ | |
| @@ -23,8 +23,12 @@ import { homepageState } from './state.svelte'; | |||||
| 23 | // REQUEST TRACKING | 23 | // REQUEST TRACKING | ||
| 24 | // ═══════════════════════════════════════════════════════════════════════════════ | 24 | // ═══════════════════════════════════════════════════════════════════════════════ | ||
| 25 | 25 | ||||
| 26 | - | interface PendingRequest<T = unknown> { | 26 | + | // Pending requests store resolvers for different response types. |
| 27 | - | resolve: (value: T) => void; | 27 | + | // The resolve function accepts `unknown` since requests with different |
| 28 | + | // response types share the same map. Type safety is maintained at the | |||
| 29 | + | // sendMessage<T> API level. | |||
| 30 | + | interface PendingRequest { | |||
| 31 | + | resolve: (value: unknown) => void; | |||
| 28 | reject: (error: Error) => void; | 32 | reject: (error: Error) => void; | ||
| 29 | timeout: ReturnType<typeof setTimeout>; | 33 | timeout: ReturnType<typeof setTimeout>; | ||
| 30 | } | 34 | } | ||
| @@ -38,8 +42,8 @@ let callbacksInitialized = false; | |||||
| 38 | // WEBKIT BRIDGE UTILITIES | 42 | // WEBKIT BRIDGE UTILITIES | ||
| 39 | // ═══════════════════════════════════════════════════════════════════════════════ | 43 | // ═══════════════════════════════════════════════════════════════════════════════ | ||
| 40 | 44 | ||||
| 41 | - | function getWebKitBridge(): { postMessage: (msg: unknown) => void } | null { | 45 | + | function getWebKitBridge(): DumberBridge | null { |
| 42 | - | const bridge = (window as any).webkit?.messageHandlers?.dumber; | 46 | + | const bridge = window.webkit?.messageHandlers?.dumber; |
| 43 | if (bridge && typeof bridge.postMessage === 'function') { | 47 | if (bridge && typeof bridge.postMessage === 'function') { | ||
| 44 | return bridge; | 48 | return bridge; | ||
| 45 | } | 49 | } | ||
| @@ -47,7 +51,7 @@ function getWebKitBridge(): { postMessage: (msg: unknown) => void } | null { | |||||
| 47 | } | 51 | } | ||
| 48 | 52 | ||||
| 49 | function getWebViewId(): number { | 53 | function getWebViewId(): number { | ||
| 50 | - | return (window as any).__dumber_webview_id || 0; | 54 | + | return window.__dumber_webview_id ?? 0; |
| 51 | } | 55 | } | ||
| 52 | 56 | ||||
| 53 | function generateRequestId(type: string): string { | 57 | function generateRequestId(type: string): string { | ||
| @@ -62,9 +66,7 @@ function initializeCallbacks(): void { | |||||
| 62 | if (callbacksInitialized) return; | 66 | if (callbacksInitialized) return; | ||
| 63 | 67 | ||||
| 64 | // Generic response handler - accepts a single Response object from Go backend | 68 | // Generic response handler - accepts a single Response object from Go backend | ||
| 65 | - | (window as any).__dumber_homepage_response = ( | 69 | + | window.__dumber_homepage_response = (response) => { |
| 66 | - | response: { requestId: string; success: boolean; data?: unknown; error?: string } | |||
| 67 | - | ) => { | |||
| 68 | const request = pendingRequests.get(response.requestId); | 70 | const request = pendingRequests.get(response.requestId); | ||
| 69 | if (!request) return; | 71 | if (!request) return; | ||
| 70 | 72 | ||||
| @@ -74,61 +76,60 @@ function initializeCallbacks(): void { | |||||
| 74 | if (response.success) { | 76 | if (response.success) { | ||
| 75 | request.resolve(response.data); | 77 | request.resolve(response.data); | ||
| 76 | } else { | 78 | } else { | ||
| 77 | - | request.reject(new Error(response.error || 'Unknown error')); | 79 | + | request.reject(new Error(response.error ?? 'Unknown error')); |
| 78 | } | 80 | } | ||
| 79 | }; | 81 | }; | ||
| 80 | 82 | ||||
| 81 | // Legacy callbacks for backward compatibility | 83 | // Legacy callbacks for backward compatibility | ||
| 82 | // These can receive either raw data or a Response object | 84 | // These can receive either raw data or a Response object | ||
| 83 | - | (window as any).__dumber_history_timeline = (dataOrResponse: unknown) => { | 85 | + | window.__dumber_history_timeline = (dataOrResponse) => { |
| 84 | handleLegacyOrResponseCallback(dataOrResponse); | 86 | handleLegacyOrResponseCallback(dataOrResponse); | ||
| 85 | }; | 87 | }; | ||
| 86 | 88 | ||||
| 87 | - | (window as any).__dumber_folders = (dataOrResponse: unknown) => { | 89 | + | window.__dumber_folders = (dataOrResponse) => { |
| 88 | handleLegacyOrResponseCallback(dataOrResponse); | 90 | handleLegacyOrResponseCallback(dataOrResponse); | ||
| 89 | }; | 91 | }; | ||
| 90 | 92 | ||||
| 91 | - | (window as any).__dumber_tags = (dataOrResponse: unknown) => { | 93 | + | window.__dumber_tags = (dataOrResponse) => { |
| 92 | handleLegacyOrResponseCallback(dataOrResponse); | 94 | handleLegacyOrResponseCallback(dataOrResponse); | ||
| 93 | }; | 95 | }; | ||
| 94 | 96 | ||||
| 95 | - | (window as any).__dumber_favorites = (dataOrResponse: unknown) => { | 97 | + | window.__dumber_favorites = (dataOrResponse) => { |
| 96 | handleLegacyOrResponseCallback(dataOrResponse); | 98 | handleLegacyOrResponseCallback(dataOrResponse); | ||
| 97 | }; | 99 | }; | ||
| 98 | 100 | ||||
| 99 | - | (window as any).__dumber_analytics = (dataOrResponse: unknown) => { | 101 | + | window.__dumber_analytics = (dataOrResponse) => { |
| 100 | handleLegacyOrResponseCallback(dataOrResponse); | 102 | handleLegacyOrResponseCallback(dataOrResponse); | ||
| 101 | }; | 103 | }; | ||
| 102 | 104 | ||||
| 103 | - | (window as any).__dumber_domain_stats = (dataOrResponse: unknown) => { | 105 | + | window.__dumber_domain_stats = (dataOrResponse) => { |
| 104 | handleLegacyOrResponseCallback(dataOrResponse); | 106 | handleLegacyOrResponseCallback(dataOrResponse); | ||
| 105 | }; | 107 | }; | ||
| 106 | 108 | ||||
| 107 | - | (window as any).__dumber_history_search_results = (dataOrResponse: unknown) => { | 109 | + | window.__dumber_history_search_results = (dataOrResponse) => { |
| 108 | handleLegacyOrResponseCallback(dataOrResponse); | 110 | handleLegacyOrResponseCallback(dataOrResponse); | ||
| 109 | }; | 111 | }; | ||
| 110 | 112 | ||||
| 111 | - | (window as any).__dumber_history_deleted = (dataOrResponse: unknown) => { | 113 | + | window.__dumber_history_deleted = (dataOrResponse) => { |
| 112 | handleLegacyOrResponseCallback(dataOrResponse); | 114 | handleLegacyOrResponseCallback(dataOrResponse); | ||
| 113 | }; | 115 | }; | ||
| 114 | 116 | ||||
| 115 | - | (window as any).__dumber_history_cleared = (dataOrResponse: unknown) => { | 117 | + | window.__dumber_history_cleared = (dataOrResponse) => { |
| 116 | handleLegacyOrResponseCallback(dataOrResponse); | 118 | handleLegacyOrResponseCallback(dataOrResponse); | ||
| 117 | }; | 119 | }; | ||
| 118 | 120 | ||||
| 119 | - | (window as any).__dumber_error = (errorOrResponse: unknown) => { | 121 | + | window.__dumber_error = (errorOrResponse) => { |
| 120 | // Handle both formats: direct error string or Response object with error | 122 | // Handle both formats: direct error string or Response object with error | ||
| 121 | - | const response = errorOrResponse as { requestId?: string; error?: string } | string; | 123 | + | if (typeof errorOrResponse === 'string') { |
| 122 | - | if (typeof response === 'string') { | |||
| 123 | // Legacy format: direct error string | 124 | // Legacy format: direct error string | ||
| 124 | - | console.error('[messaging] Legacy error:', response); | 125 | + | console.error('[messaging] Legacy error:', errorOrResponse); |
| 125 | - | } else if (response && typeof response === 'object') { | 126 | + | } else if (errorOrResponse && typeof errorOrResponse === 'object') { |
| 126 | // Response object format | 127 | // Response object format | ||
| 127 | - | const request = pendingRequests.get(response.requestId || 'default'); | 128 | + | const request = pendingRequests.get(errorOrResponse.requestId ?? 'default'); |
| 128 | if (request) { | 129 | if (request) { | ||
| 129 | clearTimeout(request.timeout); | 130 | clearTimeout(request.timeout); | ||
| 130 | - | pendingRequests.delete(response.requestId || 'default'); | 131 | + | pendingRequests.delete(errorOrResponse.requestId ?? 'default'); |
| 131 | - | request.reject(new Error(response.error || 'Unknown error')); | 132 | + | request.reject(new Error(errorOrResponse.error ?? 'Unknown error')); |
| 132 | } | 133 | } | ||
| 133 | } | 134 | } | ||
| 134 | }; | 135 | }; | ||
| @@ -188,7 +189,13 @@ async function sendMessage<T = unknown>( | |||||
| 188 | reject(new Error(`Request timeout: ${type}`)); | 189 | reject(new Error(`Request timeout: ${type}`)); | ||
| 189 | }, REQUEST_TIMEOUT_MS); | 190 | }, REQUEST_TIMEOUT_MS); | ||
| 190 | 191 | ||||
| 191 | - | pendingRequests.set(requestId, { resolve: resolve as any, reject, timeout }); | 192 | + | // Cast resolve to accept unknown - type safety is maintained at API level |
| 193 | + | // since sendMessage<T> returns Promise<T> and callers expect typed responses | |||
| 194 | + | pendingRequests.set(requestId, { | |||
| 195 | + | resolve: resolve as (value: unknown) => void, | |||
| 196 | + | reject, | |||
| 197 | + | timeout, | |||
| 198 | + | }); | |||
| 192 | 199 | ||||
| 193 | try { | 200 | try { | ||
| 194 | bridge.postMessage({ | 201 | bridge.postMessage({ | ||
M webui/src/pages/homepage/types.ts +3 -1
| @@ -3,6 +3,8 @@ | |||
| 3 | 3 | // Mirrors Go models from internal/db/models.go and related query files | |
| 4 | 4 | // ═══════════════════════════════════════════════════════════════════════════════ | |
| 5 | 5 | ||
| 6 | + | import type { Component } from 'svelte'; | |
| 7 | + | ||
| 6 | 8 | // ───────────────────────────────────────────────────────────────────────────── | |
| 7 | 9 | // History Types | |
| 8 | 10 | // ───────────────────────────────────────────────────────────────────────────── | |
| @@ -110,7 +112,7 @@ export interface CommandPaletteItem { | |||
| 110 | 112 | id: string; | |
| 111 | 113 | label: string; | |
| 112 | 114 | description?: string; | |
| 113 | - | icon?: import('svelte').Component | string; | |
| 115 | + | icon?: Component | string; | |
| 114 | 116 | action: () => void; | |
| 115 | 117 | shortcut?: string; | |
| 116 | 118 | } | |
| @@ -3,6 +3,8 @@ | |||||
| 3 | // Mirrors Go models from internal/db/models.go and related query files | 3 | // Mirrors Go models from internal/db/models.go and related query files | ||
| 4 | // ═══════════════════════════════════════════════════════════════════════════════ | 4 | // ═══════════════════════════════════════════════════════════════════════════════ | ||
| 5 | 5 | ||||
| 6 | + | import type { Component } from 'svelte'; | |||
| 7 | + | ||||
| 6 | // ───────────────────────────────────────────────────────────────────────────── | 8 | // ───────────────────────────────────────────────────────────────────────────── | ||
| 7 | // History Types | 9 | // History Types | ||
| 8 | // ───────────────────────────────────────────────────────────────────────────── | 10 | // ───────────────────────────────────────────────────────────────────────────── | ||
| @@ -110,7 +112,7 @@ export interface CommandPaletteItem { | |||||
| 110 | id: string; | 112 | id: string; | ||
| 111 | label: string; | 113 | label: string; | ||
| 112 | description?: string; | 114 | description?: string; | ||
| 113 | - | icon?: import('svelte').Component | string; | 115 | + | icon?: Component | string; |
| 114 | action: () => void; | 116 | action: () => void; | ||
| 115 | shortcut?: string; | 117 | shortcut?: string; | ||
| 116 | } | 118 | } | ||
A webui/src/types/dumber.d.ts +150 -0
| @@ -0,0 +1,150 @@ | |||
| 1 | + | /** | |
| 2 | + | * Global type declarations for Dumber browser WebKit bridge | |
| 3 | + | */ | |
| 4 | + | ||
| 5 | + | // ═══════════════════════════════════════════════════════════════════════════════ | |
| 6 | + | // WEBKIT BRIDGE TYPES | |
| 7 | + | // ═══════════════════════════════════════════════════════════════════════════════ | |
| 8 | + | ||
| 9 | + | interface DumberBridge { | |
| 10 | + | postMessage: (message: unknown) => void; | |
| 11 | + | } | |
| 12 | + | ||
| 13 | + | interface DumberWebKit { | |
| 14 | + | messageHandlers?: { | |
| 15 | + | dumber?: DumberBridge; | |
| 16 | + | }; | |
| 17 | + | } | |
| 18 | + | ||
| 19 | + | // ═══════════════════════════════════════════════════════════════════════════════ | |
| 20 | + | // VIEW TRANSITION API (not yet in TypeScript DOM lib) | |
| 21 | + | // ═══════════════════════════════════════════════════════════════════════════════ | |
| 22 | + | ||
| 23 | + | interface ViewTransition { | |
| 24 | + | finished: Promise<void>; | |
| 25 | + | ready: Promise<void>; | |
| 26 | + | updateCallbackDone: Promise<void>; | |
| 27 | + | } | |
| 28 | + | ||
| 29 | + | // ═══════════════════════════════════════════════════════════════════════════════ | |
| 30 | + | // THEME TYPES | |
| 31 | + | // ═══════════════════════════════════════════════════════════════════════════════ | |
| 32 | + | ||
| 33 | + | type ThemeMode = 'light' | 'dark'; | |
| 34 | + | ||
| 35 | + | interface ColorSchemeManager { | |
| 36 | + | setUserPreference?: (theme: ThemeMode) => void; | |
| 37 | + | } | |
| 38 | + | ||
| 39 | + | // ═══════════════════════════════════════════════════════════════════════════════ | |
| 40 | + | // HOMEPAGE MESSAGING TYPES | |
| 41 | + | // ═══════════════════════════════════════════════════════════════════════════════ | |
| 42 | + | ||
| 43 | + | /** Response from Go backend for homepage requests */ | |
| 44 | + | interface HomepageResponse { | |
| 45 | + | requestId: string; | |
| 46 | + | success: boolean; | |
| 47 | + | data?: unknown; | |
| 48 | + | error?: string; | |
| 49 | + | } | |
| 50 | + | ||
| 51 | + | /** Error response format (can be string for legacy or object) */ | |
| 52 | + | type HomepageErrorResponse = string | { requestId?: string; error?: string }; | |
| 53 | + | ||
| 54 | + | /** Callback type for homepage responses */ | |
| 55 | + | type HomepageResponseCallback = (response: HomepageResponse) => void; | |
| 56 | + | ||
| 57 | + | /** Callback type for legacy data responses (can receive raw data or Response object) */ | |
| 58 | + | type HomepageLegacyCallback = (dataOrResponse: unknown) => void; | |
| 59 | + | ||
| 60 | + | /** Callback type for error responses */ | |
| 61 | + | type HomepageErrorCallback = (errorOrResponse: HomepageErrorResponse) => void; | |
| 62 | + | ||
| 63 | + | // ═══════════════════════════════════════════════════════════════════════════════ | |
| 64 | + | // GLOBAL DECLARATIONS | |
| 65 | + | // ═══════════════════════════════════════════════════════════════════════════════ | |
| 66 | + | ||
| 67 | + | declare global { | |
| 68 | + | // Re-export bridge type for use in other modules | |
| 69 | + | type DumberBridge = { | |
| 70 | + | postMessage: (message: unknown) => void; | |
| 71 | + | }; | |
| 72 | + | interface Window { | |
| 73 | + | // ───────────────────────────────────────────────────────────────────────── | |
| 74 | + | // Core WebKit Bridge | |
| 75 | + | // ───────────────────────────────────────────────────────────────────────── | |
| 76 | + | /** WebView ID assigned by the browser backend */ | |
| 77 | + | __dumber_webview_id?: number; | |
| 78 | + | /** WebKit message handlers for native bridge */ | |
| 79 | + | webkit?: DumberWebKit; | |
| 80 | + | ||
| 81 | + | // ───────────────────────────────────────────────────────────────────────── | |
| 82 | + | // Config Page Callbacks | |
| 83 | + | // ───────────────────────────────────────────────────────────────────────── | |
| 84 | + | /** Callback invoked when config save succeeds */ | |
| 85 | + | __dumber_config_saved?: (resp?: unknown) => void; | |
| 86 | + | /** Callback invoked when config save fails */ | |
| 87 | + | __dumber_config_error?: (msg: unknown) => void; | |
| 88 | + | ||
| 89 | + | // ───────────────────────────────────────────────────────────────────────── | |
| 90 | + | // Keybindings Callbacks | |
| 91 | + | // ───────────────────────────────────────────────────────────────────────── | |
| 92 | + | /** Callback invoked when keybindings are loaded */ | |
| 93 | + | __dumber_keybindings_loaded?: (resp?: unknown) => void; | |
| 94 | + | /** Callback invoked when keybindings load fails */ | |
| 95 | + | __dumber_keybindings_error?: (msg: unknown) => void; | |
| 96 | + | /** Callback invoked when a keybinding is set */ | |
| 97 | + | __dumber_keybinding_set?: (resp?: unknown) => void; | |
| 98 | + | /** Callback invoked when keybinding set fails */ | |
| 99 | + | __dumber_keybinding_set_error?: (msg: unknown) => void; | |
| 100 | + | /** Callback invoked when a keybinding is reset */ | |
| 101 | + | __dumber_keybinding_reset?: (resp?: unknown) => void; | |
| 102 | + | /** Callback invoked when keybinding reset fails */ | |
| 103 | + | __dumber_keybinding_reset_error?: (msg: unknown) => void; | |
| 104 | + | /** Callback invoked when all keybindings are reset */ | |
| 105 | + | __dumber_keybindings_reset_all?: (resp?: unknown) => void; | |
| 106 | + | /** Callback invoked when reset all keybindings fails */ | |
| 107 | + | __dumber_keybindings_reset_all_error?: (msg: unknown) => void; | |
| 108 | + | ||
| 109 | + | // ───────────────────────────────────────────────────────────────────────── | |
| 110 | + | // Homepage Callbacks | |
| 111 | + | // ───────────────────────────────────────────────────────────────────────── | |
| 112 | + | /** Generic response handler for homepage requests */ | |
| 113 | + | __dumber_homepage_response?: HomepageResponseCallback; | |
| 114 | + | /** History timeline data callback */ | |
| 115 | + | __dumber_history_timeline?: HomepageLegacyCallback; | |
| 116 | + | /** Folders data callback */ | |
| 117 | + | __dumber_folders?: HomepageLegacyCallback; | |
| 118 | + | /** Tags data callback */ | |
| 119 | + | __dumber_tags?: HomepageLegacyCallback; | |
| 120 | + | /** Favorites data callback */ | |
| 121 | + | __dumber_favorites?: HomepageLegacyCallback; | |
| 122 | + | /** Analytics data callback */ | |
| 123 | + | __dumber_analytics?: HomepageLegacyCallback; | |
| 124 | + | /** Domain stats data callback */ | |
| 125 | + | __dumber_domain_stats?: HomepageLegacyCallback; | |
| 126 | + | /** History search results callback */ | |
| 127 | + | __dumber_history_search_results?: HomepageLegacyCallback; | |
| 128 | + | /** History deleted callback */ | |
| 129 | + | __dumber_history_deleted?: HomepageLegacyCallback; | |
| 130 | + | /** History cleared callback */ | |
| 131 | + | __dumber_history_cleared?: HomepageLegacyCallback; | |
| 132 | + | /** Error callback for homepage requests */ | |
| 133 | + | __dumber_error?: HomepageErrorCallback; | |
| 134 | + | ||
| 135 | + | // ───────────────────────────────────────────────────────────────────────── | |
| 136 | + | // Theme Management | |
| 137 | + | // ───────────────────────────────────────────────────────────────────────── | |
| 138 | + | /** Color scheme manager for theme switching */ | |
| 139 | + | __dumber_color_scheme_manager?: ColorSchemeManager; | |
| 140 | + | /** Legacy theme setter */ | |
| 141 | + | __dumber_setTheme?: (theme: ThemeMode) => void; | |
| 142 | + | } | |
| 143 | + | ||
| 144 | + | interface Document { | |
| 145 | + | /** View Transition API */ | |
| 146 | + | startViewTransition?: (callback: () => void | Promise<void>) => ViewTransition; | |
| 147 | + | } | |
| 148 | + | } | |
| 149 | + | ||
| 150 | + | export {}; | |
| @@ -0,0 +1,150 @@ | |||||
| 1 | + | /** | |||
| 2 | + | * Global type declarations for Dumber browser WebKit bridge | |||
| 3 | + | */ | |||
| 4 | + | ||||
| 5 | + | // ═══════════════════════════════════════════════════════════════════════════════ | |||
| 6 | + | // WEBKIT BRIDGE TYPES | |||
| 7 | + | // ═══════════════════════════════════════════════════════════════════════════════ | |||
| 8 | + | ||||
| 9 | + | interface DumberBridge { | |||
| 10 | + | postMessage: (message: unknown) => void; | |||
| 11 | + | } | |||
| 12 | + | ||||
| 13 | + | interface DumberWebKit { | |||
| 14 | + | messageHandlers?: { | |||
| 15 | + | dumber?: DumberBridge; | |||
| 16 | + | }; | |||
| 17 | + | } | |||
| 18 | + | ||||
| 19 | + | // ═══════════════════════════════════════════════════════════════════════════════ | |||
| 20 | + | // VIEW TRANSITION API (not yet in TypeScript DOM lib) | |||
| 21 | + | // ═══════════════════════════════════════════════════════════════════════════════ | |||
| 22 | + | ||||
| 23 | + | interface ViewTransition { | |||
| 24 | + | finished: Promise<void>; | |||
| 25 | + | ready: Promise<void>; | |||
| 26 | + | updateCallbackDone: Promise<void>; | |||
| 27 | + | } | |||
| 28 | + | ||||
| 29 | + | // ═══════════════════════════════════════════════════════════════════════════════ | |||
| 30 | + | // THEME TYPES | |||
| 31 | + | // ═══════════════════════════════════════════════════════════════════════════════ | |||
| 32 | + | ||||
| 33 | + | type ThemeMode = 'light' | 'dark'; | |||
| 34 | + | ||||
| 35 | + | interface ColorSchemeManager { | |||
| 36 | + | setUserPreference?: (theme: ThemeMode) => void; | |||
| 37 | + | } | |||
| 38 | + | ||||
| 39 | + | // ═══════════════════════════════════════════════════════════════════════════════ | |||
| 40 | + | // HOMEPAGE MESSAGING TYPES | |||
| 41 | + | // ═══════════════════════════════════════════════════════════════════════════════ | |||
| 42 | + | ||||
| 43 | + | /** Response from Go backend for homepage requests */ | |||
| 44 | + | interface HomepageResponse { | |||
| 45 | + | requestId: string; | |||
| 46 | + | success: boolean; | |||
| 47 | + | data?: unknown; | |||
| 48 | + | error?: string; | |||
| 49 | + | } | |||
| 50 | + | ||||
| 51 | + | /** Error response format (can be string for legacy or object) */ | |||
| 52 | + | type HomepageErrorResponse = string | { requestId?: string; error?: string }; | |||
| 53 | + | ||||
| 54 | + | /** Callback type for homepage responses */ | |||
| 55 | + | type HomepageResponseCallback = (response: HomepageResponse) => void; | |||
| 56 | + | ||||
| 57 | + | /** Callback type for legacy data responses (can receive raw data or Response object) */ | |||
| 58 | + | type HomepageLegacyCallback = (dataOrResponse: unknown) => void; | |||
| 59 | + | ||||
| 60 | + | /** Callback type for error responses */ | |||
| 61 | + | type HomepageErrorCallback = (errorOrResponse: HomepageErrorResponse) => void; | |||
| 62 | + | ||||
| 63 | + | // ═══════════════════════════════════════════════════════════════════════════════ | |||
| 64 | + | // GLOBAL DECLARATIONS | |||
| 65 | + | // ═══════════════════════════════════════════════════════════════════════════════ | |||
| 66 | + | ||||
| 67 | + | declare global { | |||
| 68 | + | // Re-export bridge type for use in other modules | |||
| 69 | + | type DumberBridge = { | |||
| 70 | + | postMessage: (message: unknown) => void; | |||
| 71 | + | }; | |||
| 72 | + | interface Window { | |||
| 73 | + | // ───────────────────────────────────────────────────────────────────────── | |||
| 74 | + | // Core WebKit Bridge | |||
| 75 | + | // ───────────────────────────────────────────────────────────────────────── | |||
| 76 | + | /** WebView ID assigned by the browser backend */ | |||
| 77 | + | __dumber_webview_id?: number; | |||
| 78 | + | /** WebKit message handlers for native bridge */ | |||
| 79 | + | webkit?: DumberWebKit; | |||
| 80 | + | ||||
| 81 | + | // ───────────────────────────────────────────────────────────────────────── | |||
| 82 | + | // Config Page Callbacks | |||
| 83 | + | // ───────────────────────────────────────────────────────────────────────── | |||
| 84 | + | /** Callback invoked when config save succeeds */ | |||
| 85 | + | __dumber_config_saved?: (resp?: unknown) => void; | |||
| 86 | + | /** Callback invoked when config save fails */ | |||
| 87 | + | __dumber_config_error?: (msg: unknown) => void; | |||
| 88 | + | ||||
| 89 | + | // ───────────────────────────────────────────────────────────────────────── | |||
| 90 | + | // Keybindings Callbacks | |||
| 91 | + | // ───────────────────────────────────────────────────────────────────────── | |||
| 92 | + | /** Callback invoked when keybindings are loaded */ | |||
| 93 | + | __dumber_keybindings_loaded?: (resp?: unknown) => void; | |||
| 94 | + | /** Callback invoked when keybindings load fails */ | |||
| 95 | + | __dumber_keybindings_error?: (msg: unknown) => void; | |||
| 96 | + | /** Callback invoked when a keybinding is set */ | |||
| 97 | + | __dumber_keybinding_set?: (resp?: unknown) => void; | |||
| 98 | + | /** Callback invoked when keybinding set fails */ | |||
| 99 | + | __dumber_keybinding_set_error?: (msg: unknown) => void; | |||
| 100 | + | /** Callback invoked when a keybinding is reset */ | |||
| 101 | + | __dumber_keybinding_reset?: (resp?: unknown) => void; | |||
| 102 | + | /** Callback invoked when keybinding reset fails */ | |||
| 103 | + | __dumber_keybinding_reset_error?: (msg: unknown) => void; | |||
| 104 | + | /** Callback invoked when all keybindings are reset */ | |||
| 105 | + | __dumber_keybindings_reset_all?: (resp?: unknown) => void; | |||
| 106 | + | /** Callback invoked when reset all keybindings fails */ | |||
| 107 | + | __dumber_keybindings_reset_all_error?: (msg: unknown) => void; | |||
| 108 | + | ||||
| 109 | + | // ───────────────────────────────────────────────────────────────────────── | |||
| 110 | + | // Homepage Callbacks | |||
| 111 | + | // ───────────────────────────────────────────────────────────────────────── | |||
| 112 | + | /** Generic response handler for homepage requests */ | |||
| 113 | + | __dumber_homepage_response?: HomepageResponseCallback; | |||
| 114 | + | /** History timeline data callback */ | |||
| 115 | + | __dumber_history_timeline?: HomepageLegacyCallback; | |||
| 116 | + | /** Folders data callback */ | |||
| 117 | + | __dumber_folders?: HomepageLegacyCallback; | |||
| 118 | + | /** Tags data callback */ | |||
| 119 | + | __dumber_tags?: HomepageLegacyCallback; | |||
| 120 | + | /** Favorites data callback */ | |||
| 121 | + | __dumber_favorites?: HomepageLegacyCallback; | |||
| 122 | + | /** Analytics data callback */ | |||
| 123 | + | __dumber_analytics?: HomepageLegacyCallback; | |||
| 124 | + | /** Domain stats data callback */ | |||
| 125 | + | __dumber_domain_stats?: HomepageLegacyCallback; | |||
| 126 | + | /** History search results callback */ | |||
| 127 | + | __dumber_history_search_results?: HomepageLegacyCallback; | |||
| 128 | + | /** History deleted callback */ | |||
| 129 | + | __dumber_history_deleted?: HomepageLegacyCallback; | |||
| 130 | + | /** History cleared callback */ | |||
| 131 | + | __dumber_history_cleared?: HomepageLegacyCallback; | |||
| 132 | + | /** Error callback for homepage requests */ | |||
| 133 | + | __dumber_error?: HomepageErrorCallback; | |||
| 134 | + | ||||
| 135 | + | // ───────────────────────────────────────────────────────────────────────── | |||
| 136 | + | // Theme Management | |||
| 137 | + | // ───────────────────────────────────────────────────────────────────────── | |||
| 138 | + | /** Color scheme manager for theme switching */ | |||
| 139 | + | __dumber_color_scheme_manager?: ColorSchemeManager; | |||
| 140 | + | /** Legacy theme setter */ | |||
| 141 | + | __dumber_setTheme?: (theme: ThemeMode) => void; | |||
| 142 | + | } | |||
| 143 | + | ||||
| 144 | + | interface Document { | |||
| 145 | + | /** View Transition API */ | |||
| 146 | + | startViewTransition?: (callback: () => void | Promise<void>) => ViewTransition; | |||
| 147 | + | } | |||
| 148 | + | } | |||
| 149 | + | ||||
| 150 | + | export {}; | |||