@runanywhere/web 0.1.0-beta.4 → 0.1.0-beta.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/Foundation/StructOffsets.d.ts +33 -5
- package/dist/Foundation/StructOffsets.d.ts.map +1 -1
- package/dist/Foundation/StructOffsets.js +128 -94
- package/dist/Foundation/StructOffsets.js.map +1 -1
- package/dist/Infrastructure/ExtensionPoint.d.ts +78 -0
- package/dist/Infrastructure/ExtensionPoint.d.ts.map +1 -0
- package/dist/Infrastructure/ExtensionPoint.js +118 -0
- package/dist/Infrastructure/ExtensionPoint.js.map +1 -0
- package/dist/Infrastructure/LocalFileStorage.d.ts +116 -0
- package/dist/Infrastructure/LocalFileStorage.d.ts.map +1 -0
- package/dist/Infrastructure/LocalFileStorage.js +368 -0
- package/dist/Infrastructure/LocalFileStorage.js.map +1 -0
- package/dist/Infrastructure/ModelDownloader.d.ts +16 -5
- package/dist/Infrastructure/ModelDownloader.d.ts.map +1 -1
- package/dist/Infrastructure/ModelDownloader.js +54 -7
- package/dist/Infrastructure/ModelDownloader.js.map +1 -1
- package/dist/Infrastructure/ModelFileInference.d.ts +39 -0
- package/dist/Infrastructure/ModelFileInference.d.ts.map +1 -0
- package/dist/Infrastructure/ModelFileInference.js +110 -0
- package/dist/Infrastructure/ModelFileInference.js.map +1 -0
- package/dist/Infrastructure/ModelLoaderTypes.d.ts +76 -11
- package/dist/Infrastructure/ModelLoaderTypes.d.ts.map +1 -1
- package/dist/Infrastructure/ModelLoaderTypes.js +7 -1
- package/dist/Infrastructure/ModelLoaderTypes.js.map +1 -1
- package/dist/Infrastructure/ModelManager.d.ts +30 -104
- package/dist/Infrastructure/ModelManager.d.ts.map +1 -1
- package/dist/Infrastructure/ModelManager.js +209 -544
- package/dist/Infrastructure/ModelManager.js.map +1 -1
- package/dist/Infrastructure/ModelRegistry.d.ts +6 -0
- package/dist/Infrastructure/ModelRegistry.d.ts.map +1 -1
- package/dist/Infrastructure/ModelRegistry.js +11 -0
- package/dist/Infrastructure/ModelRegistry.js.map +1 -1
- package/dist/Public/Extensions/RunAnywhere+VoicePipeline.d.ts +3 -29
- package/dist/Public/Extensions/RunAnywhere+VoicePipeline.d.ts.map +1 -1
- package/dist/Public/Extensions/RunAnywhere+VoicePipeline.js +46 -42
- package/dist/Public/Extensions/RunAnywhere+VoicePipeline.js.map +1 -1
- package/dist/Public/Extensions/VoicePipelineTypes.d.ts +28 -37
- package/dist/Public/Extensions/VoicePipelineTypes.d.ts.map +1 -1
- package/dist/Public/Extensions/VoicePipelineTypes.js +4 -1
- package/dist/Public/Extensions/VoicePipelineTypes.js.map +1 -1
- package/dist/Public/RunAnywhere.d.ts +65 -61
- package/dist/Public/RunAnywhere.d.ts.map +1 -1
- package/dist/Public/RunAnywhere.js +193 -141
- package/dist/Public/RunAnywhere.js.map +1 -1
- package/dist/index.d.ts +23 -39
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -31
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.ts +0 -1
- package/dist/types/index.d.ts.map +1 -1
- package/dist/types/index.js +1 -0
- package/dist/types/index.js.map +1 -1
- package/package.json +1 -5
- package/wasm/racommons.js +91 -91
- package/wasm/racommons.wasm +0 -0
- package/dist/Foundation/SherpaONNXBridge.d.ts +0 -147
- package/dist/Foundation/SherpaONNXBridge.d.ts.map +0 -1
- package/dist/Foundation/SherpaONNXBridge.js +0 -345
- package/dist/Foundation/SherpaONNXBridge.js.map +0 -1
- package/dist/Infrastructure/AudioCapture.d.ts +0 -87
- package/dist/Infrastructure/AudioCapture.d.ts.map +0 -1
- package/dist/Infrastructure/AudioCapture.js +0 -231
- package/dist/Infrastructure/AudioCapture.js.map +0 -1
- package/dist/Infrastructure/AudioPlayback.d.ts +0 -53
- package/dist/Infrastructure/AudioPlayback.d.ts.map +0 -1
- package/dist/Infrastructure/AudioPlayback.js +0 -117
- package/dist/Infrastructure/AudioPlayback.js.map +0 -1
- package/dist/Infrastructure/VLMWorkerBridge.d.ts +0 -211
- package/dist/Infrastructure/VLMWorkerBridge.d.ts.map +0 -1
- package/dist/Infrastructure/VLMWorkerBridge.js +0 -264
- package/dist/Infrastructure/VLMWorkerBridge.js.map +0 -1
- package/dist/Infrastructure/VLMWorkerRuntime.d.ts +0 -38
- package/dist/Infrastructure/VLMWorkerRuntime.d.ts.map +0 -1
- package/dist/Infrastructure/VLMWorkerRuntime.js +0 -503
- package/dist/Infrastructure/VLMWorkerRuntime.js.map +0 -1
- package/dist/Infrastructure/VideoCapture.d.ts +0 -118
- package/dist/Infrastructure/VideoCapture.d.ts.map +0 -1
- package/dist/Infrastructure/VideoCapture.js +0 -207
- package/dist/Infrastructure/VideoCapture.js.map +0 -1
- package/dist/Public/Extensions/DiffusionTypes.d.ts +0 -64
- package/dist/Public/Extensions/DiffusionTypes.d.ts.map +0 -1
- package/dist/Public/Extensions/DiffusionTypes.js +0 -28
- package/dist/Public/Extensions/DiffusionTypes.js.map +0 -1
- package/dist/Public/Extensions/EmbeddingsTypes.d.ts +0 -33
- package/dist/Public/Extensions/EmbeddingsTypes.d.ts.map +0 -1
- package/dist/Public/Extensions/EmbeddingsTypes.js +0 -13
- package/dist/Public/Extensions/EmbeddingsTypes.js.map +0 -1
- package/dist/Public/Extensions/RunAnywhere+Diffusion.d.ts +0 -44
- package/dist/Public/Extensions/RunAnywhere+Diffusion.d.ts.map +0 -1
- package/dist/Public/Extensions/RunAnywhere+Diffusion.js +0 -189
- package/dist/Public/Extensions/RunAnywhere+Diffusion.js.map +0 -1
- package/dist/Public/Extensions/RunAnywhere+Embeddings.d.ts +0 -56
- package/dist/Public/Extensions/RunAnywhere+Embeddings.d.ts.map +0 -1
- package/dist/Public/Extensions/RunAnywhere+Embeddings.js +0 -240
- package/dist/Public/Extensions/RunAnywhere+Embeddings.js.map +0 -1
- package/dist/Public/Extensions/RunAnywhere+STT.d.ts +0 -95
- package/dist/Public/Extensions/RunAnywhere+STT.d.ts.map +0 -1
- package/dist/Public/Extensions/RunAnywhere+STT.js +0 -417
- package/dist/Public/Extensions/RunAnywhere+STT.js.map +0 -1
- package/dist/Public/Extensions/RunAnywhere+StructuredOutput.d.ts +0 -69
- package/dist/Public/Extensions/RunAnywhere+StructuredOutput.d.ts.map +0 -1
- package/dist/Public/Extensions/RunAnywhere+StructuredOutput.js +0 -196
- package/dist/Public/Extensions/RunAnywhere+StructuredOutput.js.map +0 -1
- package/dist/Public/Extensions/RunAnywhere+TTS.d.ts +0 -55
- package/dist/Public/Extensions/RunAnywhere+TTS.d.ts.map +0 -1
- package/dist/Public/Extensions/RunAnywhere+TTS.js +0 -253
- package/dist/Public/Extensions/RunAnywhere+TTS.js.map +0 -1
- package/dist/Public/Extensions/RunAnywhere+TextGeneration.d.ts +0 -80
- package/dist/Public/Extensions/RunAnywhere+TextGeneration.d.ts.map +0 -1
- package/dist/Public/Extensions/RunAnywhere+TextGeneration.js +0 -470
- package/dist/Public/Extensions/RunAnywhere+TextGeneration.js.map +0 -1
- package/dist/Public/Extensions/RunAnywhere+ToolCalling.d.ts +0 -82
- package/dist/Public/Extensions/RunAnywhere+ToolCalling.d.ts.map +0 -1
- package/dist/Public/Extensions/RunAnywhere+ToolCalling.js +0 -576
- package/dist/Public/Extensions/RunAnywhere+ToolCalling.js.map +0 -1
- package/dist/Public/Extensions/RunAnywhere+VAD.d.ts +0 -70
- package/dist/Public/Extensions/RunAnywhere+VAD.d.ts.map +0 -1
- package/dist/Public/Extensions/RunAnywhere+VAD.js +0 -231
- package/dist/Public/Extensions/RunAnywhere+VAD.js.map +0 -1
- package/dist/Public/Extensions/RunAnywhere+VLM.d.ts +0 -58
- package/dist/Public/Extensions/RunAnywhere+VLM.d.ts.map +0 -1
- package/dist/Public/Extensions/RunAnywhere+VLM.js +0 -262
- package/dist/Public/Extensions/RunAnywhere+VLM.js.map +0 -1
- package/dist/Public/Extensions/STTTypes.d.ts +0 -53
- package/dist/Public/Extensions/STTTypes.d.ts.map +0 -1
- package/dist/Public/Extensions/STTTypes.js +0 -16
- package/dist/Public/Extensions/STTTypes.js.map +0 -1
- package/dist/Public/Extensions/TTSTypes.d.ts +0 -31
- package/dist/Public/Extensions/TTSTypes.d.ts.map +0 -1
- package/dist/Public/Extensions/TTSTypes.js +0 -3
- package/dist/Public/Extensions/TTSTypes.js.map +0 -1
- package/dist/Public/Extensions/ToolCallingTypes.d.ts +0 -78
- package/dist/Public/Extensions/ToolCallingTypes.d.ts.map +0 -1
- package/dist/Public/Extensions/ToolCallingTypes.js +0 -8
- package/dist/Public/Extensions/ToolCallingTypes.js.map +0 -1
- package/dist/Public/Extensions/VADTypes.d.ts +0 -30
- package/dist/Public/Extensions/VADTypes.d.ts.map +0 -1
- package/dist/Public/Extensions/VADTypes.js +0 -8
- package/dist/Public/Extensions/VADTypes.js.map +0 -1
- package/dist/Public/Extensions/VLMTypes.d.ts +0 -56
- package/dist/Public/Extensions/VLMTypes.d.ts.map +0 -1
- package/dist/Public/Extensions/VLMTypes.js +0 -24
- package/dist/Public/Extensions/VLMTypes.js.map +0 -1
- package/dist/types/LLMTypes.d.ts +0 -48
- package/dist/types/LLMTypes.d.ts.map +0 -1
- package/dist/types/LLMTypes.js +0 -8
- package/dist/types/LLMTypes.js.map +0 -1
- package/dist/workers/vlm-worker.d.ts +0 -9
- package/dist/workers/vlm-worker.d.ts.map +0 -1
- package/dist/workers/vlm-worker.js +0 -10
- package/dist/workers/vlm-worker.js.map +0 -1
- package/wasm/racommons-webgpu.js +0 -156
- package/wasm/racommons-webgpu.wasm +0 -0
- package/wasm/sherpa/sherpa-onnx-asr.js +0 -1538
- package/wasm/sherpa/sherpa-onnx-glue-original.js +0 -19
- package/wasm/sherpa/sherpa-onnx-glue.js +0 -17
- package/wasm/sherpa/sherpa-onnx-tts.js +0 -657
- package/wasm/sherpa/sherpa-onnx-vad.js +0 -337
- package/wasm/sherpa/sherpa-onnx-wave.js +0 -88
- package/wasm/sherpa/sherpa-onnx.wasm +0 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LocalFileStorage - Persistent model storage using the File System Access API
|
|
3
|
+
*
|
|
4
|
+
* Allows users to choose a real folder on their local filesystem for model
|
|
5
|
+
* storage. Models are saved as actual files (e.g. ~/ai-models/smollm2.gguf)
|
|
6
|
+
* that persist permanently — no browser eviction, no re-downloads.
|
|
7
|
+
*
|
|
8
|
+
* The directory handle is stored in IndexedDB (not cookies — handles are
|
|
9
|
+
* structured-cloneable objects that can't be serialized to strings).
|
|
10
|
+
*
|
|
11
|
+
* Browser support:
|
|
12
|
+
* - Chrome 122+: Full support with persistent permissions ("Allow on every visit")
|
|
13
|
+
* - Chrome 86-121: Supported but re-prompts for permission each session
|
|
14
|
+
* - Edge: Same as Chrome (Chromium-based)
|
|
15
|
+
* - Firefox/Safari: NOT supported — falls back to OPFS storage
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* const storage = new LocalFileStorage();
|
|
19
|
+
* await storage.chooseDirectory(); // User picks a folder
|
|
20
|
+
* await storage.saveModel('model-id', data);
|
|
21
|
+
*
|
|
22
|
+
* // On return visit:
|
|
23
|
+
* const restored = await storage.restoreDirectory();
|
|
24
|
+
* if (restored) {
|
|
25
|
+
* const data = await storage.loadModel('model-id');
|
|
26
|
+
* }
|
|
27
|
+
*/
|
|
28
|
+
export declare class LocalFileStorage {
|
|
29
|
+
private dirHandle;
|
|
30
|
+
private _isReady;
|
|
31
|
+
private _hasStoredHandle;
|
|
32
|
+
/** Whether the File System Access API is available in this browser. */
|
|
33
|
+
static get isSupported(): boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Get the stored directory name from localStorage (fast, synchronous).
|
|
36
|
+
* Available immediately on page load before IndexedDB restores the handle.
|
|
37
|
+
* Returns the folder name only (e.g. "ai-models"), not the full path.
|
|
38
|
+
*/
|
|
39
|
+
static get storedDirectoryName(): string | null;
|
|
40
|
+
/** Whether the storage is ready for use (directory selected + permission granted). */
|
|
41
|
+
get isReady(): boolean;
|
|
42
|
+
/** Whether a handle was found in IndexedDB (even if permission isn't granted yet). */
|
|
43
|
+
get hasStoredHandle(): boolean;
|
|
44
|
+
/** The name of the chosen directory (for display in UI). */
|
|
45
|
+
get directoryName(): string | null;
|
|
46
|
+
/**
|
|
47
|
+
* Prompt the user to choose a directory for model storage.
|
|
48
|
+
* Opens the OS folder picker dialog.
|
|
49
|
+
* Stores the directory handle in IndexedDB for future sessions.
|
|
50
|
+
*
|
|
51
|
+
* @returns true if a directory was selected, false if cancelled
|
|
52
|
+
*/
|
|
53
|
+
chooseDirectory(): Promise<boolean>;
|
|
54
|
+
/**
|
|
55
|
+
* Attempt to restore a previously chosen directory from IndexedDB.
|
|
56
|
+
*
|
|
57
|
+
* On Chrome 122+, if the user selected "Allow on every visit",
|
|
58
|
+
* permission is automatically granted without any prompt.
|
|
59
|
+
*
|
|
60
|
+
* @returns true if directory was restored and permission is granted.
|
|
61
|
+
* false if no handle stored or permission not granted (UI
|
|
62
|
+
* should show a "Re-authorize" button).
|
|
63
|
+
*/
|
|
64
|
+
restoreDirectory(): Promise<boolean>;
|
|
65
|
+
/**
|
|
66
|
+
* Request readwrite permission on a previously stored handle.
|
|
67
|
+
* MUST be called from a user gesture (button click handler).
|
|
68
|
+
*
|
|
69
|
+
* @returns true if permission was granted
|
|
70
|
+
*/
|
|
71
|
+
requestAccess(): Promise<boolean>;
|
|
72
|
+
/**
|
|
73
|
+
* Save model data to the local filesystem.
|
|
74
|
+
* @param key - Model identifier (used as filename)
|
|
75
|
+
* @param data - Model file data
|
|
76
|
+
*/
|
|
77
|
+
saveModel(key: string, data: ArrayBuffer): Promise<void>;
|
|
78
|
+
/**
|
|
79
|
+
* Load model data from the local filesystem.
|
|
80
|
+
* @param key - Model identifier
|
|
81
|
+
* @returns Model data, or null if not found
|
|
82
|
+
*/
|
|
83
|
+
loadModel(key: string): Promise<ArrayBuffer | null>;
|
|
84
|
+
/**
|
|
85
|
+
* Check if a model file exists in local storage.
|
|
86
|
+
* @param key - Model identifier
|
|
87
|
+
*/
|
|
88
|
+
hasModel(key: string): Promise<boolean>;
|
|
89
|
+
/**
|
|
90
|
+
* Delete a model file from local storage.
|
|
91
|
+
* @param key - Model identifier
|
|
92
|
+
*/
|
|
93
|
+
deleteModel(key: string): Promise<void>;
|
|
94
|
+
/**
|
|
95
|
+
* Get file size without reading into memory.
|
|
96
|
+
* @param key - Model identifier
|
|
97
|
+
*/
|
|
98
|
+
getFileSize(key: string): Promise<number | null>;
|
|
99
|
+
/**
|
|
100
|
+
* List all model files in the directory.
|
|
101
|
+
*/
|
|
102
|
+
listModels(): Promise<Array<{
|
|
103
|
+
id: string;
|
|
104
|
+
sizeBytes: number;
|
|
105
|
+
lastModified: number;
|
|
106
|
+
}>>;
|
|
107
|
+
private openDB;
|
|
108
|
+
private storeHandle;
|
|
109
|
+
private retrieveHandle;
|
|
110
|
+
/**
|
|
111
|
+
* Sanitize a key for use as a filename.
|
|
112
|
+
* Keeps alphanumeric, dots, dashes, underscores. Replaces everything else.
|
|
113
|
+
*/
|
|
114
|
+
private sanitizeFilename;
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=LocalFileStorage.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LocalFileStorage.d.ts","sourceRoot":"","sources":["../../src/Infrastructure/LocalFileStorage.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAoCH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,SAAS,CAA0C;IAC3D,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,gBAAgB,CAAS;IAMjC,uEAAuE;IACvE,MAAM,KAAK,WAAW,IAAI,OAAO,CAEhC;IAED;;;;OAIG;IACH,MAAM,KAAK,mBAAmB,IAAI,MAAM,GAAG,IAAI,CAM9C;IAMD,sFAAsF;IACtF,IAAI,OAAO,IAAI,OAAO,CAErB;IAED,sFAAsF;IACtF,IAAI,eAAe,IAAI,OAAO,CAE7B;IAED,4DAA4D;IAC5D,IAAI,aAAa,IAAI,MAAM,GAAG,IAAI,CAEjC;IAMD;;;;;;OAMG;IACG,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAiCzC;;;;;;;;;OASG;IACG,gBAAgB,IAAI,OAAO,CAAC,OAAO,CAAC;IAkC1C;;;;;OAKG;IACG,aAAa,IAAI,OAAO,CAAC,OAAO,CAAC;IA0BvC;;;;OAIG;IACG,SAAS,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAmB9D;;;;OAIG;IACG,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC;IAczD;;;OAGG;IACG,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAY7C;;;OAGG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAY7C;;;OAGG;IACG,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAatD;;OAEG;IACG,UAAU,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;YAwB7E,MAAM;YAgBN,WAAW;YAiBX,cAAc;IAsB5B;;;OAGG;IACH,OAAO,CAAC,gBAAgB;CAGzB"}
|
|
@@ -0,0 +1,368 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LocalFileStorage - Persistent model storage using the File System Access API
|
|
3
|
+
*
|
|
4
|
+
* Allows users to choose a real folder on their local filesystem for model
|
|
5
|
+
* storage. Models are saved as actual files (e.g. ~/ai-models/smollm2.gguf)
|
|
6
|
+
* that persist permanently — no browser eviction, no re-downloads.
|
|
7
|
+
*
|
|
8
|
+
* The directory handle is stored in IndexedDB (not cookies — handles are
|
|
9
|
+
* structured-cloneable objects that can't be serialized to strings).
|
|
10
|
+
*
|
|
11
|
+
* Browser support:
|
|
12
|
+
* - Chrome 122+: Full support with persistent permissions ("Allow on every visit")
|
|
13
|
+
* - Chrome 86-121: Supported but re-prompts for permission each session
|
|
14
|
+
* - Edge: Same as Chrome (Chromium-based)
|
|
15
|
+
* - Firefox/Safari: NOT supported — falls back to OPFS storage
|
|
16
|
+
*
|
|
17
|
+
* Usage:
|
|
18
|
+
* const storage = new LocalFileStorage();
|
|
19
|
+
* await storage.chooseDirectory(); // User picks a folder
|
|
20
|
+
* await storage.saveModel('model-id', data);
|
|
21
|
+
*
|
|
22
|
+
* // On return visit:
|
|
23
|
+
* const restored = await storage.restoreDirectory();
|
|
24
|
+
* if (restored) {
|
|
25
|
+
* const data = await storage.loadModel('model-id');
|
|
26
|
+
* }
|
|
27
|
+
*/
|
|
28
|
+
import { SDKLogger } from '../Foundation/SDKLogger';
|
|
29
|
+
const logger = new SDKLogger('LocalFileStorage');
|
|
30
|
+
/* eslint-enable @typescript-eslint/no-explicit-any */
|
|
31
|
+
// ---------------------------------------------------------------------------
|
|
32
|
+
// IndexedDB Constants
|
|
33
|
+
// ---------------------------------------------------------------------------
|
|
34
|
+
const DB_NAME = 'runanywhere-storage';
|
|
35
|
+
const DB_VERSION = 1;
|
|
36
|
+
const STORE_NAME = 'handles';
|
|
37
|
+
const HANDLE_KEY = 'modelDirectory';
|
|
38
|
+
const LS_DIR_NAME_KEY = 'runanywhere_storage_dir_name';
|
|
39
|
+
// ---------------------------------------------------------------------------
|
|
40
|
+
// LocalFileStorage
|
|
41
|
+
// ---------------------------------------------------------------------------
|
|
42
|
+
export class LocalFileStorage {
|
|
43
|
+
dirHandle = null;
|
|
44
|
+
_isReady = false;
|
|
45
|
+
_hasStoredHandle = false;
|
|
46
|
+
// -------------------------------------------------------------------------
|
|
47
|
+
// Static
|
|
48
|
+
// -------------------------------------------------------------------------
|
|
49
|
+
/** Whether the File System Access API is available in this browser. */
|
|
50
|
+
static get isSupported() {
|
|
51
|
+
return typeof window !== 'undefined' && 'showDirectoryPicker' in window;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Get the stored directory name from localStorage (fast, synchronous).
|
|
55
|
+
* Available immediately on page load before IndexedDB restores the handle.
|
|
56
|
+
* Returns the folder name only (e.g. "ai-models"), not the full path.
|
|
57
|
+
*/
|
|
58
|
+
static get storedDirectoryName() {
|
|
59
|
+
try {
|
|
60
|
+
return localStorage.getItem(LS_DIR_NAME_KEY);
|
|
61
|
+
}
|
|
62
|
+
catch {
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// -------------------------------------------------------------------------
|
|
67
|
+
// State
|
|
68
|
+
// -------------------------------------------------------------------------
|
|
69
|
+
/** Whether the storage is ready for use (directory selected + permission granted). */
|
|
70
|
+
get isReady() {
|
|
71
|
+
return this._isReady && this.dirHandle !== null;
|
|
72
|
+
}
|
|
73
|
+
/** Whether a handle was found in IndexedDB (even if permission isn't granted yet). */
|
|
74
|
+
get hasStoredHandle() {
|
|
75
|
+
return this._hasStoredHandle;
|
|
76
|
+
}
|
|
77
|
+
/** The name of the chosen directory (for display in UI). */
|
|
78
|
+
get directoryName() {
|
|
79
|
+
return this.dirHandle?.name ?? null;
|
|
80
|
+
}
|
|
81
|
+
// -------------------------------------------------------------------------
|
|
82
|
+
// Directory Selection
|
|
83
|
+
// -------------------------------------------------------------------------
|
|
84
|
+
/**
|
|
85
|
+
* Prompt the user to choose a directory for model storage.
|
|
86
|
+
* Opens the OS folder picker dialog.
|
|
87
|
+
* Stores the directory handle in IndexedDB for future sessions.
|
|
88
|
+
*
|
|
89
|
+
* @returns true if a directory was selected, false if cancelled
|
|
90
|
+
*/
|
|
91
|
+
async chooseDirectory() {
|
|
92
|
+
if (!LocalFileStorage.isSupported) {
|
|
93
|
+
logger.warning('File System Access API not supported in this browser');
|
|
94
|
+
return false;
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
// showDirectoryPicker requires user gesture (button click)
|
|
98
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
99
|
+
this.dirHandle = await window.showDirectoryPicker({
|
|
100
|
+
mode: 'readwrite',
|
|
101
|
+
});
|
|
102
|
+
await this.storeHandle(this.dirHandle);
|
|
103
|
+
this._isReady = true;
|
|
104
|
+
this._hasStoredHandle = true;
|
|
105
|
+
// Persist directory name in localStorage for fast UI display on next visit
|
|
106
|
+
try {
|
|
107
|
+
localStorage.setItem(LS_DIR_NAME_KEY, this.dirHandle.name);
|
|
108
|
+
}
|
|
109
|
+
catch { /* non-critical */ }
|
|
110
|
+
logger.info(`Local storage directory selected: ${this.dirHandle.name}`);
|
|
111
|
+
return true;
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
if (err instanceof Error && err.name === 'AbortError') {
|
|
115
|
+
logger.debug('User cancelled directory picker');
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
119
|
+
logger.error(`Failed to choose directory: ${message}`);
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
/**
|
|
124
|
+
* Attempt to restore a previously chosen directory from IndexedDB.
|
|
125
|
+
*
|
|
126
|
+
* On Chrome 122+, if the user selected "Allow on every visit",
|
|
127
|
+
* permission is automatically granted without any prompt.
|
|
128
|
+
*
|
|
129
|
+
* @returns true if directory was restored and permission is granted.
|
|
130
|
+
* false if no handle stored or permission not granted (UI
|
|
131
|
+
* should show a "Re-authorize" button).
|
|
132
|
+
*/
|
|
133
|
+
async restoreDirectory() {
|
|
134
|
+
if (!LocalFileStorage.isSupported)
|
|
135
|
+
return false;
|
|
136
|
+
try {
|
|
137
|
+
const handle = await this.retrieveHandle();
|
|
138
|
+
if (!handle) {
|
|
139
|
+
logger.debug('No stored directory handle found');
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
this._hasStoredHandle = true;
|
|
143
|
+
this.dirHandle = handle;
|
|
144
|
+
// queryPermission does NOT require user gesture
|
|
145
|
+
const permission = await handle.queryPermission({ mode: 'readwrite' });
|
|
146
|
+
if (permission === 'granted') {
|
|
147
|
+
this._isReady = true;
|
|
148
|
+
logger.info(`Local storage restored: ${handle.name}`);
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
// Permission not granted yet — user needs to click a button
|
|
152
|
+
// to trigger requestPermission() via requestAccess()
|
|
153
|
+
logger.debug(`Local storage handle found but permission is '${permission}' — needs re-authorization`);
|
|
154
|
+
this._isReady = false;
|
|
155
|
+
return false;
|
|
156
|
+
}
|
|
157
|
+
catch (err) {
|
|
158
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
159
|
+
logger.warning(`Failed to restore directory: ${message}`);
|
|
160
|
+
return false;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Request readwrite permission on a previously stored handle.
|
|
165
|
+
* MUST be called from a user gesture (button click handler).
|
|
166
|
+
*
|
|
167
|
+
* @returns true if permission was granted
|
|
168
|
+
*/
|
|
169
|
+
async requestAccess() {
|
|
170
|
+
if (!this.dirHandle) {
|
|
171
|
+
logger.warning('No directory handle to request access for');
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
const permission = await this.dirHandle.requestPermission({ mode: 'readwrite' });
|
|
176
|
+
if (permission === 'granted') {
|
|
177
|
+
this._isReady = true;
|
|
178
|
+
logger.info(`Local storage re-authorized: ${this.dirHandle.name}`);
|
|
179
|
+
return true;
|
|
180
|
+
}
|
|
181
|
+
logger.debug(`Permission request result: ${permission}`);
|
|
182
|
+
return false;
|
|
183
|
+
}
|
|
184
|
+
catch (err) {
|
|
185
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
186
|
+
logger.warning(`Permission request failed: ${message}`);
|
|
187
|
+
return false;
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
// -------------------------------------------------------------------------
|
|
191
|
+
// Model Operations
|
|
192
|
+
// -------------------------------------------------------------------------
|
|
193
|
+
/**
|
|
194
|
+
* Save model data to the local filesystem.
|
|
195
|
+
* @param key - Model identifier (used as filename)
|
|
196
|
+
* @param data - Model file data
|
|
197
|
+
*/
|
|
198
|
+
async saveModel(key, data) {
|
|
199
|
+
if (!this.dirHandle || !this._isReady) {
|
|
200
|
+
throw new Error('LocalFileStorage not ready — call chooseDirectory() or restoreDirectory() first.');
|
|
201
|
+
}
|
|
202
|
+
const filename = this.sanitizeFilename(key);
|
|
203
|
+
const fileHandle = await this.dirHandle.getFileHandle(filename, { create: true });
|
|
204
|
+
const writable = await fileHandle.createWritable();
|
|
205
|
+
try {
|
|
206
|
+
await writable.write(data);
|
|
207
|
+
await writable.close();
|
|
208
|
+
logger.info(`Saved model to local storage: ${filename} (${(data.byteLength / 1024 / 1024).toFixed(1)} MB)`);
|
|
209
|
+
}
|
|
210
|
+
catch (err) {
|
|
211
|
+
try {
|
|
212
|
+
await writable.abort();
|
|
213
|
+
}
|
|
214
|
+
catch { /* ignore */ }
|
|
215
|
+
throw err;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* Load model data from the local filesystem.
|
|
220
|
+
* @param key - Model identifier
|
|
221
|
+
* @returns Model data, or null if not found
|
|
222
|
+
*/
|
|
223
|
+
async loadModel(key) {
|
|
224
|
+
if (!this.dirHandle || !this._isReady)
|
|
225
|
+
return null;
|
|
226
|
+
try {
|
|
227
|
+
const filename = this.sanitizeFilename(key);
|
|
228
|
+
const fileHandle = await this.dirHandle.getFileHandle(filename);
|
|
229
|
+
const file = await fileHandle.getFile();
|
|
230
|
+
logger.info(`Loaded model from local storage: ${filename} (${(file.size / 1024 / 1024).toFixed(1)} MB)`);
|
|
231
|
+
return await file.arrayBuffer();
|
|
232
|
+
}
|
|
233
|
+
catch {
|
|
234
|
+
return null; // File not found
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Check if a model file exists in local storage.
|
|
239
|
+
* @param key - Model identifier
|
|
240
|
+
*/
|
|
241
|
+
async hasModel(key) {
|
|
242
|
+
if (!this.dirHandle || !this._isReady)
|
|
243
|
+
return false;
|
|
244
|
+
try {
|
|
245
|
+
const filename = this.sanitizeFilename(key);
|
|
246
|
+
await this.dirHandle.getFileHandle(filename);
|
|
247
|
+
return true;
|
|
248
|
+
}
|
|
249
|
+
catch {
|
|
250
|
+
return false;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Delete a model file from local storage.
|
|
255
|
+
* @param key - Model identifier
|
|
256
|
+
*/
|
|
257
|
+
async deleteModel(key) {
|
|
258
|
+
if (!this.dirHandle || !this._isReady)
|
|
259
|
+
return;
|
|
260
|
+
try {
|
|
261
|
+
const filename = this.sanitizeFilename(key);
|
|
262
|
+
await this.dirHandle.removeEntry(filename);
|
|
263
|
+
logger.info(`Deleted model from local storage: ${filename}`);
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
// File doesn't exist
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
/**
|
|
270
|
+
* Get file size without reading into memory.
|
|
271
|
+
* @param key - Model identifier
|
|
272
|
+
*/
|
|
273
|
+
async getFileSize(key) {
|
|
274
|
+
if (!this.dirHandle || !this._isReady)
|
|
275
|
+
return null;
|
|
276
|
+
try {
|
|
277
|
+
const filename = this.sanitizeFilename(key);
|
|
278
|
+
const fileHandle = await this.dirHandle.getFileHandle(filename);
|
|
279
|
+
const file = await fileHandle.getFile();
|
|
280
|
+
return file.size;
|
|
281
|
+
}
|
|
282
|
+
catch {
|
|
283
|
+
return null;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
/**
|
|
287
|
+
* List all model files in the directory.
|
|
288
|
+
*/
|
|
289
|
+
async listModels() {
|
|
290
|
+
if (!this.dirHandle || !this._isReady)
|
|
291
|
+
return [];
|
|
292
|
+
const models = [];
|
|
293
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
294
|
+
for await (const [name, handle] of this.dirHandle.entries()) {
|
|
295
|
+
if (handle.kind === 'file') {
|
|
296
|
+
const file = await handle.getFile();
|
|
297
|
+
models.push({
|
|
298
|
+
id: name,
|
|
299
|
+
sizeBytes: file.size,
|
|
300
|
+
lastModified: file.lastModified,
|
|
301
|
+
});
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return models;
|
|
305
|
+
}
|
|
306
|
+
// -------------------------------------------------------------------------
|
|
307
|
+
// IndexedDB Handle Persistence
|
|
308
|
+
// -------------------------------------------------------------------------
|
|
309
|
+
async openDB() {
|
|
310
|
+
return new Promise((resolve, reject) => {
|
|
311
|
+
const request = indexedDB.open(DB_NAME, DB_VERSION);
|
|
312
|
+
request.onupgradeneeded = () => {
|
|
313
|
+
const db = request.result;
|
|
314
|
+
if (!db.objectStoreNames.contains(STORE_NAME)) {
|
|
315
|
+
db.createObjectStore(STORE_NAME);
|
|
316
|
+
}
|
|
317
|
+
};
|
|
318
|
+
request.onsuccess = () => resolve(request.result);
|
|
319
|
+
request.onerror = () => reject(request.error);
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
async storeHandle(handle) {
|
|
323
|
+
try {
|
|
324
|
+
const db = await this.openDB();
|
|
325
|
+
const tx = db.transaction(STORE_NAME, 'readwrite');
|
|
326
|
+
tx.objectStore(STORE_NAME).put(handle, HANDLE_KEY);
|
|
327
|
+
await new Promise((resolve, reject) => {
|
|
328
|
+
tx.oncomplete = () => resolve();
|
|
329
|
+
tx.onerror = () => reject(tx.error);
|
|
330
|
+
});
|
|
331
|
+
db.close();
|
|
332
|
+
logger.debug('Directory handle stored in IndexedDB');
|
|
333
|
+
}
|
|
334
|
+
catch (err) {
|
|
335
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
336
|
+
logger.warning(`Failed to store handle in IndexedDB: ${message}`);
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
async retrieveHandle() {
|
|
340
|
+
try {
|
|
341
|
+
const db = await this.openDB();
|
|
342
|
+
const tx = db.transaction(STORE_NAME, 'readonly');
|
|
343
|
+
const handle = await new Promise((resolve, reject) => {
|
|
344
|
+
const req = tx.objectStore(STORE_NAME).get(HANDLE_KEY);
|
|
345
|
+
req.onsuccess = () => resolve(req.result ?? null);
|
|
346
|
+
req.onerror = () => reject(req.error);
|
|
347
|
+
});
|
|
348
|
+
db.close();
|
|
349
|
+
return handle;
|
|
350
|
+
}
|
|
351
|
+
catch (err) {
|
|
352
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
353
|
+
logger.warning(`Failed to retrieve handle from IndexedDB: ${message}`);
|
|
354
|
+
return null;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
// -------------------------------------------------------------------------
|
|
358
|
+
// Helpers
|
|
359
|
+
// -------------------------------------------------------------------------
|
|
360
|
+
/**
|
|
361
|
+
* Sanitize a key for use as a filename.
|
|
362
|
+
* Keeps alphanumeric, dots, dashes, underscores. Replaces everything else.
|
|
363
|
+
*/
|
|
364
|
+
sanitizeFilename(key) {
|
|
365
|
+
return key.replace(/[<>:"/\\|?*\x00-\x1F]/g, '_');
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
//# sourceMappingURL=LocalFileStorage.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LocalFileStorage.js","sourceRoot":"","sources":["../../src/Infrastructure/LocalFileStorage.ts"],"names":[],"mappings":"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"}
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
import { OPFSStorage } from './OPFSStorage';
|
|
9
9
|
import type { MetadataMap } from './OPFSStorage';
|
|
10
|
+
import type { LocalFileStorage } from './LocalFileStorage';
|
|
10
11
|
import type { ManagedModel } from './ModelRegistry';
|
|
11
12
|
import type { ModelRegistry } from './ModelRegistry';
|
|
12
13
|
/** Candidate model that could be evicted to free space. */
|
|
@@ -33,6 +34,11 @@ export interface QuotaCheckResult {
|
|
|
33
34
|
export declare class ModelDownloader {
|
|
34
35
|
private readonly storage;
|
|
35
36
|
private readonly registry;
|
|
37
|
+
/**
|
|
38
|
+
* Optional local filesystem storage. When configured, models are saved
|
|
39
|
+
* to the user's chosen directory instead of OPFS. Set via setLocalFileStorage().
|
|
40
|
+
*/
|
|
41
|
+
private localFileStorage;
|
|
36
42
|
/**
|
|
37
43
|
* In-memory fallback cache for models that were downloaded successfully
|
|
38
44
|
* but failed to persist to OPFS (e.g. storage quota exceeded).
|
|
@@ -40,6 +46,11 @@ export declare class ModelDownloader {
|
|
|
40
46
|
*/
|
|
41
47
|
private readonly memoryCache;
|
|
42
48
|
constructor(registry: ModelRegistry, storage: OPFSStorage);
|
|
49
|
+
/**
|
|
50
|
+
* Set the local file storage backend.
|
|
51
|
+
* When configured and ready, models are saved/loaded from the local filesystem first.
|
|
52
|
+
*/
|
|
53
|
+
setLocalFileStorage(storage: LocalFileStorage): void;
|
|
43
54
|
/**
|
|
44
55
|
* Check whether a model will fit in OPFS without eviction.
|
|
45
56
|
*
|
|
@@ -64,7 +75,7 @@ export declare class ModelDownloader {
|
|
|
64
75
|
* URLs are validated before fetching to prevent SSRF and enforce HTTPS.
|
|
65
76
|
*/
|
|
66
77
|
downloadFile(url: string, onProgress?: (progress: number, bytesDownloaded: number, totalBytes: number) => void): Promise<Uint8Array>;
|
|
67
|
-
/** Store data
|
|
78
|
+
/** Store data, preferring local filesystem when available, then OPFS, then memory cache. */
|
|
68
79
|
storeInOPFS(key: string, data: Uint8Array): Promise<void>;
|
|
69
80
|
/**
|
|
70
81
|
* Evict old models from OPFS to free space for a new model.
|
|
@@ -76,15 +87,15 @@ export declare class ModelDownloader {
|
|
|
76
87
|
* the other, separated by "__" (e.g. "modelA" and "modelA__mmproj-...").
|
|
77
88
|
*/
|
|
78
89
|
private evictOPFSModels;
|
|
79
|
-
/** Load data from
|
|
90
|
+
/** Load data from storage. Priority: local filesystem > OPFS > memory cache. */
|
|
80
91
|
loadFromOPFS(key: string): Promise<Uint8Array | null>;
|
|
81
|
-
/** Check existence in OPFS or in-memory cache. */
|
|
92
|
+
/** Check existence in local storage, OPFS, or in-memory cache. */
|
|
82
93
|
existsInOPFS(key: string): Promise<boolean>;
|
|
83
94
|
/** Check if data exists in actual OPFS storage (NOT memory cache). */
|
|
84
95
|
existsInActualOPFS(key: string): Promise<boolean>;
|
|
85
|
-
/** Delete from OPFS
|
|
96
|
+
/** Delete from all storage backends (local filesystem, OPFS, memory cache). */
|
|
86
97
|
deleteFromOPFS(key: string): Promise<void>;
|
|
87
|
-
/** Get file size from
|
|
98
|
+
/** Get file size from storage without reading into memory. */
|
|
88
99
|
getOPFSFileSize(key: string): Promise<number | null>;
|
|
89
100
|
/**
|
|
90
101
|
* Build an OPFS key for additional files (e.g., mmproj).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ModelDownloader.d.ts","sourceRoot":"","sources":["../../src/Infrastructure/ModelDownloader.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"ModelDownloader.d.ts","sourceRoot":"","sources":["../../src/Infrastructure/ModelDownloader.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAC5C,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAE3D,OAAO,KAAK,EAAE,YAAY,EAAoB,MAAM,iBAAiB,CAAC;AACtE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAMrD,2DAA2D;AAC3D,MAAM,WAAW,qBAAqB;IACpC,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,4CAA4C;AAC5C,MAAM,WAAW,gBAAgB;IAC/B,oEAAoE;IACpE,IAAI,EAAE,OAAO,CAAC;IACd,4CAA4C;IAC5C,cAAc,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,WAAW,EAAE,MAAM,CAAC;IACpB;;;OAGG;IACH,kBAAkB,EAAE,qBAAqB,EAAE,CAAC;CAC7C;AAkED,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAc;IACtC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAgB;IAEzC;;;OAGG;IACH,OAAO,CAAC,gBAAgB,CAAiC;IAEzD;;;;OAIG;IACH,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAiC;gBAEjD,QAAQ,EAAE,aAAa,EAAE,OAAO,EAAE,WAAW;IAKzD;;;OAGG;IACH,mBAAmB,CAAC,OAAO,EAAE,gBAAgB,GAAG,IAAI;IAQpD;;;;;;;;;;OAUG;IACG,iBAAiB,CACrB,KAAK,EAAE,YAAY,EACnB,QAAQ,EAAE,WAAW,EACrB,aAAa,CAAC,EAAE,MAAM,GACrB,OAAO,CAAC,gBAAgB,CAAC;IAmD5B;;;OAGG;IACG,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAyGnD;;;;;OAKG;IACG,YAAY,CAChB,GAAG,EAAE,MAAM,EACX,UAAU,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,eAAe,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,KAAK,IAAI,GACnF,OAAO,CAAC,UAAU,CAAC;IA+BtB,4FAA4F;IACtF,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC;IAoD/D;;;;;;;;OAQG;YACW,eAAe;IAmD7B,gFAAgF;IAC1E,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC;IAuC3D,kEAAkE;IAC5D,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IASjD,sEAAsE;IAChE,kBAAkB,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAIvD,+EAA+E;IACzE,cAAc,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAQhD,8DAA8D;IACxD,eAAe,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAY1D;;;;OAIG;IACH,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM;IAI5D,6DAA6D;IAC7D,OAAO,CAAC,oBAAoB;CAS7B"}
|