uilint 0.2.145 → 0.2.147
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/{chunk-ZUOFUPGT.js → chunk-5EDE3J6O.js} +2 -1
- package/dist/chunk-5QUW7BNW.js +80 -0
- package/dist/chunk-5QUW7BNW.js.map +1 -0
- package/dist/{chunk-ZOLQHFVQ.js → chunk-JEYSQ5KF.js} +20 -1
- package/dist/chunk-JEYSQ5KF.js.map +1 -0
- package/dist/index.js +475 -248
- package/dist/index.js.map +1 -1
- package/dist/{init-ui-KJYYI5DH.js → init-ui-QQXIZSKI.js} +123 -22
- package/dist/init-ui-QQXIZSKI.js.map +1 -0
- package/dist/{plugin-loader-O6PNFN6D.js → plugin-loader-LUIV7MLR.js} +2 -2
- package/dist/{remove-ui-ZHW4GUFL.js → remove-ui-GZRFA2AC.js} +2 -2
- package/dist/{render-43OMCORR.js → render-2P4YWHXV.js} +112 -97
- package/dist/render-2P4YWHXV.js.map +1 -0
- package/dist/{upgrade-TPZ62BT2.js → upgrade-2UKW3SIQ.js} +2 -2
- package/package.json +8 -8
- package/dist/chunk-WG2WZTB2.js +0 -56
- package/dist/chunk-WG2WZTB2.js.map +0 -1
- package/dist/chunk-ZOLQHFVQ.js.map +0 -1
- package/dist/init-ui-KJYYI5DH.js.map +0 -1
- package/dist/render-43OMCORR.js.map +0 -1
- /package/dist/{chunk-ZUOFUPGT.js.map → chunk-5EDE3J6O.js.map} +0 -0
- /package/dist/{plugin-loader-O6PNFN6D.js.map → plugin-loader-LUIV7MLR.js.map} +0 -0
- /package/dist/{remove-ui-ZHW4GUFL.js.map → remove-ui-GZRFA2AC.js.map} +0 -0
- /package/dist/{upgrade-TPZ62BT2.js.map → upgrade-2UKW3SIQ.js.map} +0 -0
|
@@ -4161,9 +4161,10 @@ function Spinner() {
|
|
|
4161
4161
|
|
|
4162
4162
|
export {
|
|
4163
4163
|
Spinner,
|
|
4164
|
+
registerInstaller,
|
|
4164
4165
|
getAllInstallers,
|
|
4165
4166
|
analyze,
|
|
4166
4167
|
execute,
|
|
4167
4168
|
getInjectionPoints
|
|
4168
4169
|
};
|
|
4169
|
-
//# sourceMappingURL=chunk-
|
|
4170
|
+
//# sourceMappingURL=chunk-5EDE3J6O.js.map
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
logInfo
|
|
4
|
+
} from "./chunk-CZNPG4UI.js";
|
|
5
|
+
|
|
6
|
+
// src/utils/plugin-loader.ts
|
|
7
|
+
import { createRequire } from "module";
|
|
8
|
+
import { existsSync, readFileSync } from "fs";
|
|
9
|
+
import { join } from "path";
|
|
10
|
+
import { pathToFileURL } from "url";
|
|
11
|
+
var KNOWN_PLUGIN_PACKAGES = ["uilint-vision", "uilint-semantic", "uilint-duplicates"];
|
|
12
|
+
async function importFromProject(specifier, projectPath) {
|
|
13
|
+
try {
|
|
14
|
+
const req = createRequire(join(projectPath, "package.json"));
|
|
15
|
+
const resolved = req.resolve(specifier);
|
|
16
|
+
return await import(resolved);
|
|
17
|
+
} catch {
|
|
18
|
+
}
|
|
19
|
+
const slashIdx = specifier.indexOf("/");
|
|
20
|
+
if (slashIdx === -1) return void 0;
|
|
21
|
+
const pkgName = specifier.slice(0, slashIdx);
|
|
22
|
+
const subpath = `./${specifier.slice(slashIdx + 1)}`;
|
|
23
|
+
const pkgDir = join(projectPath, "node_modules", pkgName);
|
|
24
|
+
const pkgJsonPath = join(pkgDir, "package.json");
|
|
25
|
+
if (!existsSync(pkgJsonPath)) return void 0;
|
|
26
|
+
const pkgJson = JSON.parse(readFileSync(pkgJsonPath, "utf8"));
|
|
27
|
+
const exportEntry = pkgJson.exports?.[subpath];
|
|
28
|
+
if (!exportEntry) return void 0;
|
|
29
|
+
const importPath = typeof exportEntry === "string" ? exportEntry : exportEntry.import ?? exportEntry.default;
|
|
30
|
+
if (!importPath) return void 0;
|
|
31
|
+
const fullPath = join(pkgDir, importPath);
|
|
32
|
+
if (!existsSync(fullPath)) return void 0;
|
|
33
|
+
return await import(pathToFileURL(fullPath).href);
|
|
34
|
+
}
|
|
35
|
+
async function discoverPlugins(resolveFrom) {
|
|
36
|
+
const manifests = [];
|
|
37
|
+
for (const pkg of KNOWN_PLUGIN_PACKAGES) {
|
|
38
|
+
const specifier = `${pkg}/cli-manifest`;
|
|
39
|
+
try {
|
|
40
|
+
let mod;
|
|
41
|
+
if (resolveFrom) {
|
|
42
|
+
mod = await importFromProject(specifier, resolveFrom);
|
|
43
|
+
} else {
|
|
44
|
+
mod = await import(specifier);
|
|
45
|
+
}
|
|
46
|
+
if (mod?.cliManifest) {
|
|
47
|
+
manifests.push(mod.cliManifest);
|
|
48
|
+
}
|
|
49
|
+
} catch {
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
return manifests;
|
|
53
|
+
}
|
|
54
|
+
async function loadPluginESLintRules(manifests, resolveFrom) {
|
|
55
|
+
const loaded = [];
|
|
56
|
+
for (const manifest of manifests) {
|
|
57
|
+
try {
|
|
58
|
+
await import(manifest.registerSpecifier);
|
|
59
|
+
loaded.push(manifest.packageName);
|
|
60
|
+
} catch {
|
|
61
|
+
if (resolveFrom) {
|
|
62
|
+
try {
|
|
63
|
+
await importFromProject(manifest.registerSpecifier, resolveFrom);
|
|
64
|
+
loaded.push(manifest.packageName);
|
|
65
|
+
} catch {
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (loaded.length > 0) {
|
|
71
|
+
logInfo(`Loaded plugin rules: ${loaded.join(", ")}`);
|
|
72
|
+
}
|
|
73
|
+
return loaded;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export {
|
|
77
|
+
discoverPlugins,
|
|
78
|
+
loadPluginESLintRules
|
|
79
|
+
};
|
|
80
|
+
//# sourceMappingURL=chunk-5QUW7BNW.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/plugin-loader.ts"],"sourcesContent":["/**\n * Dynamic plugin loader for the CLI.\n *\n * Probes known plugin package names for a `cli-manifest` subpath export.\n * Each manifest describes the CLI flag, help text, and registration entry\n * point, keeping the core CLI free of plugin-specific knowledge.\n */\n\nimport { createRequire } from \"module\";\nimport { existsSync, readFileSync } from \"fs\";\nimport { join } from \"path\";\nimport { pathToFileURL } from \"url\";\nimport { logInfo } from \"./prompts.js\";\n\n/**\n * Metadata a plugin exposes via its `<pkg>/cli-manifest` subpath export.\n * Plugins define a plain object matching this shape — no shared type import needed.\n */\nexport interface PluginCLIManifest {\n /** npm package name, e.g. \"uilint-vision\" */\n packageName: string;\n /** CLI flag name (without --), e.g. \"vision\" */\n cliFlag: string;\n /** Description shown in --help */\n cliDescription: string;\n /** Import specifier for the register module, e.g. \"uilint-vision/eslint-rules/register\" */\n registerSpecifier: string;\n /** Display name shown in the interactive install UI, e.g. \"Vision Analysis\" */\n displayName: string;\n /** Description shown in the interactive install UI */\n displayDescription: string;\n /** Emoji icon for the interactive install UI, e.g. \"👁️\" */\n displayIcon: string;\n}\n\n/** Package names to probe for CLI manifests */\nconst KNOWN_PLUGIN_PACKAGES = [\"uilint-vision\", \"uilint-semantic\", \"uilint-duplicates\"];\n\n/**\n * Resolve and import a package subpath specifier from a given project directory.\n *\n * First tries CJS `createRequire().resolve()`, which works for packages with\n * a `\"require\"` export condition. If that fails (e.g. the package only defines\n * `\"import\"`), falls back to manually reading the package's `exports` map and\n * constructing a file URL.\n */\nasync function importFromProject(\n specifier: string,\n projectPath: string,\n): Promise<unknown> {\n // Try CJS resolution first (fast path)\n try {\n const req = createRequire(join(projectPath, \"package.json\"));\n const resolved = req.resolve(specifier);\n return await import(resolved);\n } catch {\n // CJS resolution failed — try ESM-only export resolution\n }\n\n // Parse \"pkg/subpath\" → [\"pkg\", \"./subpath\"]\n const slashIdx = specifier.indexOf(\"/\");\n if (slashIdx === -1) return undefined;\n const pkgName = specifier.slice(0, slashIdx);\n const subpath = `./${specifier.slice(slashIdx + 1)}`;\n\n const pkgDir = join(projectPath, \"node_modules\", pkgName);\n const pkgJsonPath = join(pkgDir, \"package.json\");\n if (!existsSync(pkgJsonPath)) return undefined;\n\n const pkgJson = JSON.parse(readFileSync(pkgJsonPath, \"utf8\"));\n const exportEntry = pkgJson.exports?.[subpath];\n if (!exportEntry) return undefined;\n\n const importPath =\n typeof exportEntry === \"string\"\n ? exportEntry\n : exportEntry.import ?? exportEntry.default;\n if (!importPath) return undefined;\n\n const fullPath = join(pkgDir, importPath);\n if (!existsSync(fullPath)) return undefined;\n\n return await import(pathToFileURL(fullPath).href);\n}\n\n/**\n * Discover available plugin manifests by probing `<pkg>/cli-manifest`.\n *\n * @param resolveFrom - Optional project path to resolve plugins from.\n * When provided, resolves plugins from the project's node_modules\n * so plugins installed in the project can be found.\n * @returns Array of discovered plugin manifests\n */\nexport async function discoverPlugins(\n resolveFrom?: string,\n): Promise<PluginCLIManifest[]> {\n const manifests: PluginCLIManifest[] = [];\n\n for (const pkg of KNOWN_PLUGIN_PACKAGES) {\n const specifier = `${pkg}/cli-manifest`;\n try {\n let mod: { cliManifest?: PluginCLIManifest } | undefined;\n if (resolveFrom) {\n mod = (await importFromProject(specifier, resolveFrom)) as typeof mod;\n } else {\n mod = (await import(specifier)) as typeof mod;\n }\n if (mod?.cliManifest) {\n manifests.push(mod.cliManifest);\n }\n } catch {\n // Plugin not installed — skip silently\n }\n }\n\n return manifests;\n}\n\n/**\n * Load ESLint rules from discovered plugins by importing their register modules.\n *\n * The register modules call `registerRuleMeta()` from `uilint-eslint`, which\n * mutates the shared `ruleRegistry` array. To avoid the dual-package problem\n * (project-resolved plugin getting a different `uilint-eslint` instance), we\n * always try a bare `import()` first — this resolves from the CLI's own\n * context so both the CLI and the plugin share the same `ruleRegistry`.\n * Only falls back to project-scoped resolution when the bare import fails\n * (e.g. when running via `npx` where plugins aren't alongside the CLI).\n *\n * @param manifests - Plugin manifests (from discoverPlugins)\n * @param resolveFrom - Optional project path to resolve plugins from.\n * @returns Array of loaded plugin package names\n */\nexport async function loadPluginESLintRules(\n manifests: PluginCLIManifest[],\n resolveFrom?: string,\n): Promise<string[]> {\n const loaded: string[] = [];\n\n for (const manifest of manifests) {\n try {\n // Prefer bare import so the register module shares the CLI's uilint-eslint\n await import(manifest.registerSpecifier);\n loaded.push(manifest.packageName);\n } catch {\n // Bare import failed — try from consumer project if a path was provided\n if (resolveFrom) {\n try {\n await importFromProject(manifest.registerSpecifier, resolveFrom);\n loaded.push(manifest.packageName);\n } catch {\n // Plugin register module not available — skip silently\n }\n }\n }\n }\n\n if (loaded.length > 0) {\n logInfo(`Loaded plugin rules: ${loaded.join(\", \")}`);\n }\n\n return loaded;\n}\n"],"mappings":";;;;;;AAQA,SAAS,qBAAqB;AAC9B,SAAS,YAAY,oBAAoB;AACzC,SAAS,YAAY;AACrB,SAAS,qBAAqB;AAyB9B,IAAM,wBAAwB,CAAC,iBAAiB,mBAAmB,mBAAmB;AAUtF,eAAe,kBACb,WACA,aACkB;AAElB,MAAI;AACF,UAAM,MAAM,cAAc,KAAK,aAAa,cAAc,CAAC;AAC3D,UAAM,WAAW,IAAI,QAAQ,SAAS;AACtC,WAAO,MAAM,OAAO;AAAA,EACtB,QAAQ;AAAA,EAER;AAGA,QAAM,WAAW,UAAU,QAAQ,GAAG;AACtC,MAAI,aAAa,GAAI,QAAO;AAC5B,QAAM,UAAU,UAAU,MAAM,GAAG,QAAQ;AAC3C,QAAM,UAAU,KAAK,UAAU,MAAM,WAAW,CAAC,CAAC;AAElD,QAAM,SAAS,KAAK,aAAa,gBAAgB,OAAO;AACxD,QAAM,cAAc,KAAK,QAAQ,cAAc;AAC/C,MAAI,CAAC,WAAW,WAAW,EAAG,QAAO;AAErC,QAAM,UAAU,KAAK,MAAM,aAAa,aAAa,MAAM,CAAC;AAC5D,QAAM,cAAc,QAAQ,UAAU,OAAO;AAC7C,MAAI,CAAC,YAAa,QAAO;AAEzB,QAAM,aACJ,OAAO,gBAAgB,WACnB,cACA,YAAY,UAAU,YAAY;AACxC,MAAI,CAAC,WAAY,QAAO;AAExB,QAAM,WAAW,KAAK,QAAQ,UAAU;AACxC,MAAI,CAAC,WAAW,QAAQ,EAAG,QAAO;AAElC,SAAO,MAAM,OAAO,cAAc,QAAQ,EAAE;AAC9C;AAUA,eAAsB,gBACpB,aAC8B;AAC9B,QAAM,YAAiC,CAAC;AAExC,aAAW,OAAO,uBAAuB;AACvC,UAAM,YAAY,GAAG,GAAG;AACxB,QAAI;AACF,UAAI;AACJ,UAAI,aAAa;AACf,cAAO,MAAM,kBAAkB,WAAW,WAAW;AAAA,MACvD,OAAO;AACL,cAAO,MAAM,OAAO;AAAA,MACtB;AACA,UAAI,KAAK,aAAa;AACpB,kBAAU,KAAK,IAAI,WAAW;AAAA,MAChC;AAAA,IACF,QAAQ;AAAA,IAER;AAAA,EACF;AAEA,SAAO;AACT;AAiBA,eAAsB,sBACpB,WACA,aACmB;AACnB,QAAM,SAAmB,CAAC;AAE1B,aAAW,YAAY,WAAW;AAChC,QAAI;AAEF,YAAM,OAAO,SAAS;AACtB,aAAO,KAAK,SAAS,WAAW;AAAA,IAClC,QAAQ;AAEN,UAAI,aAAa;AACf,YAAI;AACF,gBAAM,kBAAkB,SAAS,mBAAmB,WAAW;AAC/D,iBAAO,KAAK,SAAS,WAAW;AAAA,QAClC,QAAQ;AAAA,QAER;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAEA,MAAI,OAAO,SAAS,GAAG;AACrB,YAAQ,wBAAwB,OAAO,KAAK,IAAI,CAAC,EAAE;AAAA,EACrD;AAEA,SAAO;AACT;","names":[]}
|
|
@@ -17,6 +17,7 @@ function createInitialState() {
|
|
|
17
17
|
status: "checking"
|
|
18
18
|
},
|
|
19
19
|
backgroundTasks: /* @__PURE__ */ new Map(),
|
|
20
|
+
pluginStates: /* @__PURE__ */ new Map(),
|
|
20
21
|
activities: [],
|
|
21
22
|
maxActivities: 50,
|
|
22
23
|
verbose: false,
|
|
@@ -60,6 +61,9 @@ function createDashboardStore() {
|
|
|
60
61
|
get ollamaStatus() {
|
|
61
62
|
return state.ollamaStatus;
|
|
62
63
|
},
|
|
64
|
+
get pluginStates() {
|
|
65
|
+
return state.pluginStates;
|
|
66
|
+
},
|
|
63
67
|
get activeFilter() {
|
|
64
68
|
return state.activeFilter;
|
|
65
69
|
},
|
|
@@ -199,6 +203,21 @@ function createDashboardStore() {
|
|
|
199
203
|
state = { ...state, activeFilter: order[nextIndex] };
|
|
200
204
|
notify();
|
|
201
205
|
},
|
|
206
|
+
// Plugin states
|
|
207
|
+
registerPlugin(id, name, model) {
|
|
208
|
+
const newMap = new Map(state.pluginStates);
|
|
209
|
+
newMap.set(id, { id, name, model, status: "idle" });
|
|
210
|
+
state = { ...state, pluginStates: newMap };
|
|
211
|
+
notify();
|
|
212
|
+
},
|
|
213
|
+
setPluginState(id, update) {
|
|
214
|
+
const existing = state.pluginStates.get(id);
|
|
215
|
+
if (!existing) return;
|
|
216
|
+
const newMap = new Map(state.pluginStates);
|
|
217
|
+
newMap.set(id, { ...existing, ...update });
|
|
218
|
+
state = { ...state, pluginStates: newMap };
|
|
219
|
+
notify();
|
|
220
|
+
},
|
|
202
221
|
// Ollama status
|
|
203
222
|
setOllamaStatus(status) {
|
|
204
223
|
state = { ...state, ollamaStatus: status };
|
|
@@ -226,4 +245,4 @@ function getDashboardStore() {
|
|
|
226
245
|
export {
|
|
227
246
|
getDashboardStore
|
|
228
247
|
};
|
|
229
|
-
//# sourceMappingURL=chunk-
|
|
248
|
+
//# sourceMappingURL=chunk-JEYSQ5KF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/serve/dashboard/store.ts"],"sourcesContent":["/**\n * Dashboard state store - simple event-based state management\n */\n\nimport type {\n DashboardState,\n DashboardStore,\n ActivityEntry,\n ActivityCategory,\n BackgroundTask,\n WorkspaceInfo,\n ServerStats,\n OllamaStatus,\n PluginId,\n PluginState,\n} from \"./types.js\";\n\ntype Listener = () => void;\n\nlet activityIdCounter = 0;\n\nfunction createInitialState(): DashboardState {\n return {\n isRunning: false,\n port: 9234,\n workspace: null,\n stats: {\n connectedClients: 0,\n subscriptions: 0,\n cacheEntries: 0,\n startTime: new Date(),\n },\n ollamaStatus: {\n status: \"checking\",\n },\n backgroundTasks: new Map(),\n pluginStates: new Map(),\n activities: [],\n maxActivities: 50,\n verbose: false,\n activeFilter: \"all\",\n };\n}\n\n/**\n * Create a dashboard store instance\n */\nexport function createDashboardStore(): DashboardStore & {\n subscribe: (listener: Listener) => () => void;\n getState: () => DashboardState;\n} {\n let state = createInitialState();\n const listeners = new Set<Listener>();\n\n function notify() {\n for (const listener of listeners) {\n listener();\n }\n }\n\n const store: DashboardStore & {\n subscribe: (listener: Listener) => () => void;\n getState: () => DashboardState;\n } = {\n // State getters\n get isRunning() {\n return state.isRunning;\n },\n get port() {\n return state.port;\n },\n get workspace() {\n return state.workspace;\n },\n get stats() {\n return state.stats;\n },\n get backgroundTasks() {\n return state.backgroundTasks;\n },\n get activities() {\n return state.activities;\n },\n get maxActivities() {\n return state.maxActivities;\n },\n get verbose() {\n return state.verbose;\n },\n get ollamaStatus() {\n return state.ollamaStatus;\n },\n get pluginStates() {\n return state.pluginStates;\n },\n get activeFilter() {\n return state.activeFilter;\n },\n\n // Server lifecycle\n setRunning(running: boolean) {\n state = { ...state, isRunning: running };\n if (running) {\n state.stats = { ...state.stats, startTime: new Date() };\n }\n notify();\n },\n\n setPort(port: number) {\n state = { ...state, port };\n notify();\n },\n\n setWorkspace(info: WorkspaceInfo) {\n state = { ...state, workspace: info };\n notify();\n },\n\n // Stats updates\n updateStats(partial: Partial<ServerStats>) {\n state = { ...state, stats: { ...state.stats, ...partial } };\n notify();\n },\n\n incrementClients() {\n state = {\n ...state,\n stats: {\n ...state.stats,\n connectedClients: state.stats.connectedClients + 1,\n },\n };\n notify();\n },\n\n decrementClients() {\n state = {\n ...state,\n stats: {\n ...state.stats,\n connectedClients: Math.max(0, state.stats.connectedClients - 1),\n },\n };\n notify();\n },\n\n incrementSubscriptions() {\n state = {\n ...state,\n stats: {\n ...state.stats,\n subscriptions: state.stats.subscriptions + 1,\n },\n };\n notify();\n },\n\n decrementSubscriptions() {\n state = {\n ...state,\n stats: {\n ...state.stats,\n subscriptions: Math.max(0, state.stats.subscriptions - 1),\n },\n };\n notify();\n },\n\n setCacheEntries(count: number) {\n state = {\n ...state,\n stats: { ...state.stats, cacheEntries: count },\n };\n notify();\n },\n\n // Background tasks\n setBackgroundTask(task: BackgroundTask) {\n const newTasks = new Map(state.backgroundTasks);\n newTasks.set(task.id, task);\n state = { ...state, backgroundTasks: newTasks };\n notify();\n },\n\n updateBackgroundTaskProgress(\n id: string,\n progress: number,\n current?: number,\n total?: number,\n message?: string\n ) {\n const task = state.backgroundTasks.get(id);\n if (task) {\n const newTasks = new Map(state.backgroundTasks);\n newTasks.set(id, {\n ...task,\n progress,\n current,\n total,\n message,\n status: \"running\",\n });\n state = { ...state, backgroundTasks: newTasks };\n notify();\n }\n },\n\n completeBackgroundTask(id: string, error?: string) {\n const task = state.backgroundTasks.get(id);\n if (task) {\n const newTasks = new Map(state.backgroundTasks);\n newTasks.set(id, {\n ...task,\n status: error ? \"error\" : \"complete\",\n progress: error ? task.progress : 100,\n error,\n });\n state = { ...state, backgroundTasks: newTasks };\n notify();\n }\n },\n\n // Activity logging\n addActivity(entry: Omit<ActivityEntry, \"id\" | \"timestamp\">) {\n const newEntry: ActivityEntry = {\n ...entry,\n id: `activity-${++activityIdCounter}`,\n timestamp: new Date(),\n };\n\n // Prepend new entry, trim to max\n const activities = [newEntry, ...state.activities].slice(\n 0,\n state.maxActivities\n );\n state = { ...state, activities };\n notify();\n },\n\n clearActivities() {\n state = { ...state, activities: [] };\n notify();\n },\n\n // Display options\n toggleVerbose() {\n state = { ...state, verbose: !state.verbose };\n notify();\n },\n\n // Activity filter\n cycleFilter() {\n const order: ActivityCategory[] = [\"all\", \"errors\", \"vision\", \"semantic\", \"lint\"];\n const currentIndex = order.indexOf(state.activeFilter);\n const nextIndex = (currentIndex + 1) % order.length;\n state = { ...state, activeFilter: order[nextIndex]! };\n notify();\n },\n\n // Plugin states\n registerPlugin(id: PluginId, name: string, model: string) {\n const newMap = new Map(state.pluginStates);\n newMap.set(id, { id, name, model, status: \"idle\" });\n state = { ...state, pluginStates: newMap };\n notify();\n },\n\n setPluginState(id: PluginId, update: Partial<PluginState>) {\n const existing = state.pluginStates.get(id);\n if (!existing) return;\n const newMap = new Map(state.pluginStates);\n newMap.set(id, { ...existing, ...update });\n state = { ...state, pluginStates: newMap };\n notify();\n },\n\n // Ollama status\n setOllamaStatus(status: OllamaStatus) {\n state = { ...state, ollamaStatus: status };\n notify();\n },\n\n // Subscription\n subscribe(listener: Listener) {\n listeners.add(listener);\n return () => listeners.delete(listener);\n },\n\n getState() {\n return state;\n },\n };\n\n return store;\n}\n\n// Singleton store instance for use across the application\nlet globalStore: ReturnType<typeof createDashboardStore> | null = null;\n\nexport function getDashboardStore(): ReturnType<typeof createDashboardStore> {\n if (!globalStore) {\n globalStore = createDashboardStore();\n }\n return globalStore;\n}\n\n// Reset store (useful for testing)\nexport function resetDashboardStore(): void {\n globalStore = null;\n}\n"],"mappings":"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","names":[]}
|