@gmickel/gno 0.28.2 → 0.29.1
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/README.md +10 -2
- package/package.json +1 -1
- package/src/app/constants.ts +4 -2
- package/src/cli/commands/mcp/install.ts +4 -4
- package/src/cli/commands/mcp/status.ts +7 -7
- package/src/cli/commands/skill/install.ts +5 -5
- package/src/cli/program.ts +2 -2
- package/src/collection/add.ts +10 -0
- package/src/collection/types.ts +1 -0
- package/src/config/types.ts +12 -2
- package/src/core/depth-policy.ts +1 -1
- package/src/core/file-ops.ts +203 -1
- package/src/llm/registry.ts +20 -4
- package/src/serve/AGENTS.md +16 -16
- package/src/serve/CLAUDE.md +16 -16
- package/src/serve/config-sync.ts +32 -1
- package/src/serve/connectors.ts +243 -0
- package/src/serve/context.ts +9 -0
- package/src/serve/doc-events.ts +31 -1
- package/src/serve/embed-scheduler.ts +12 -0
- package/src/serve/import-preview.ts +173 -0
- package/src/serve/public/app.tsx +101 -7
- package/src/serve/public/components/AIModelSelector.tsx +383 -145
- package/src/serve/public/components/AddCollectionDialog.tsx +123 -7
- package/src/serve/public/components/BootstrapStatus.tsx +133 -0
- package/src/serve/public/components/CaptureModal.tsx +5 -2
- package/src/serve/public/components/CollectionsEmptyState.tsx +63 -0
- package/src/serve/public/components/FirstRunWizard.tsx +622 -0
- package/src/serve/public/components/HealthCenter.tsx +128 -0
- package/src/serve/public/components/IndexingProgress.tsx +21 -2
- package/src/serve/public/components/QuickSwitcher.tsx +62 -36
- package/src/serve/public/components/TagInput.tsx +5 -1
- package/src/serve/public/components/WikiLinkAutocomplete.tsx +15 -6
- package/src/serve/public/components/WorkspaceTabs.tsx +60 -0
- package/src/serve/public/hooks/use-doc-events.ts +48 -4
- package/src/serve/public/lib/local-history.ts +40 -7
- package/src/serve/public/lib/navigation-state.ts +156 -0
- package/src/serve/public/lib/workspace-tabs.ts +235 -0
- package/src/serve/public/pages/Ask.tsx +11 -1
- package/src/serve/public/pages/Browse.tsx +73 -0
- package/src/serve/public/pages/Collections.tsx +29 -13
- package/src/serve/public/pages/Connectors.tsx +178 -0
- package/src/serve/public/pages/Dashboard.tsx +493 -67
- package/src/serve/public/pages/DocView.tsx +192 -34
- package/src/serve/public/pages/DocumentEditor.tsx +127 -5
- package/src/serve/public/pages/Search.tsx +12 -1
- package/src/serve/routes/api.ts +541 -62
- package/src/serve/server.ts +79 -2
- package/src/serve/status-model.ts +149 -0
- package/src/serve/status.ts +706 -0
- package/src/serve/watch-service.ts +73 -8
- package/src/types/electrobun-shell.d.ts +43 -0
|
@@ -8,35 +8,76 @@ import type { EmbedScheduler } from "./embed-scheduler";
|
|
|
8
8
|
|
|
9
9
|
import { defaultSyncService } from "../ingestion";
|
|
10
10
|
|
|
11
|
+
export interface CollectionWatchState {
|
|
12
|
+
expectedCollections: string[];
|
|
13
|
+
activeCollections: string[];
|
|
14
|
+
failedCollections: Array<{ collection: string; reason: string }>;
|
|
15
|
+
queuedCollections: string[];
|
|
16
|
+
syncingCollections: string[];
|
|
17
|
+
lastEventAt: string | null;
|
|
18
|
+
lastSyncAt: string | null;
|
|
19
|
+
}
|
|
20
|
+
|
|
11
21
|
interface CollectionWatchServiceOptions {
|
|
12
22
|
collections: Collection[];
|
|
13
23
|
store: SqliteAdapter;
|
|
14
24
|
scheduler: EmbedScheduler | null;
|
|
15
25
|
eventBus: DocumentEventBus;
|
|
26
|
+
watchFactory?: typeof watch;
|
|
16
27
|
}
|
|
17
28
|
|
|
18
29
|
export class CollectionWatchService {
|
|
19
|
-
|
|
30
|
+
#collections: Collection[];
|
|
20
31
|
readonly #store: SqliteAdapter;
|
|
21
32
|
readonly #scheduler: EmbedScheduler | null;
|
|
22
33
|
readonly #eventBus: DocumentEventBus;
|
|
23
|
-
readonly #watchers
|
|
34
|
+
readonly #watchers = new Map<string, FSWatcher>();
|
|
24
35
|
readonly #pendingByCollection = new Map<string, Set<string>>();
|
|
25
36
|
readonly #timers = new Map<string, ReturnType<typeof setTimeout>>();
|
|
26
37
|
readonly #syncing = new Set<string>();
|
|
27
38
|
readonly #suppressedPaths = new Map<string, number>();
|
|
39
|
+
readonly #watchFactory: typeof watch;
|
|
40
|
+
readonly #failedCollections = new Map<string, string>();
|
|
41
|
+
#lastEventAt: string | null = null;
|
|
42
|
+
#lastSyncAt: string | null = null;
|
|
28
43
|
|
|
29
44
|
constructor(options: CollectionWatchServiceOptions) {
|
|
30
45
|
this.#collections = options.collections;
|
|
31
46
|
this.#store = options.store;
|
|
32
47
|
this.#scheduler = options.scheduler;
|
|
33
48
|
this.#eventBus = options.eventBus;
|
|
49
|
+
this.#watchFactory = options.watchFactory ?? watch;
|
|
34
50
|
}
|
|
35
51
|
|
|
36
52
|
start(): void {
|
|
53
|
+
this.updateCollections(this.#collections);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
updateCollections(collections: Collection[]): void {
|
|
57
|
+
this.#collections = collections;
|
|
58
|
+
const nextNames = new Set(collections.map((collection) => collection.name));
|
|
59
|
+
|
|
60
|
+
for (const [collectionName, watcher] of this.#watchers) {
|
|
61
|
+
if (!nextNames.has(collectionName)) {
|
|
62
|
+
watcher.close();
|
|
63
|
+
this.#watchers.delete(collectionName);
|
|
64
|
+
this.#failedCollections.delete(collectionName);
|
|
65
|
+
this.#pendingByCollection.delete(collectionName);
|
|
66
|
+
const timer = this.#timers.get(collectionName);
|
|
67
|
+
if (timer) {
|
|
68
|
+
clearTimeout(timer);
|
|
69
|
+
this.#timers.delete(collectionName);
|
|
70
|
+
}
|
|
71
|
+
this.#syncing.delete(collectionName);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
37
75
|
for (const collection of this.#collections) {
|
|
76
|
+
if (this.#watchers.has(collection.name)) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
38
79
|
try {
|
|
39
|
-
const watcher =
|
|
80
|
+
const watcher = this.#watchFactory(
|
|
40
81
|
collection.path,
|
|
41
82
|
{ recursive: true },
|
|
42
83
|
(_eventType, filename) => {
|
|
@@ -47,12 +88,17 @@ export class CollectionWatchService {
|
|
|
47
88
|
if (suppressedUntil && suppressedUntil > Date.now()) {
|
|
48
89
|
return;
|
|
49
90
|
}
|
|
91
|
+
this.#lastEventAt = new Date().toISOString();
|
|
50
92
|
this.#queueChange(collection.name, relPath);
|
|
51
93
|
}
|
|
52
94
|
);
|
|
53
|
-
this.#watchers.
|
|
54
|
-
|
|
55
|
-
|
|
95
|
+
this.#watchers.set(collection.name, watcher);
|
|
96
|
+
this.#failedCollections.delete(collection.name);
|
|
97
|
+
} catch (error) {
|
|
98
|
+
this.#failedCollections.set(
|
|
99
|
+
collection.name,
|
|
100
|
+
error instanceof Error ? error.message : "watch unavailable"
|
|
101
|
+
);
|
|
56
102
|
}
|
|
57
103
|
}
|
|
58
104
|
}
|
|
@@ -65,15 +111,33 @@ export class CollectionWatchService {
|
|
|
65
111
|
for (const timer of this.#timers.values()) {
|
|
66
112
|
clearTimeout(timer);
|
|
67
113
|
}
|
|
68
|
-
for (const watcher of this.#watchers) {
|
|
114
|
+
for (const watcher of this.#watchers.values()) {
|
|
69
115
|
watcher.close();
|
|
70
116
|
}
|
|
71
117
|
this.#timers.clear();
|
|
72
|
-
this.#watchers.
|
|
118
|
+
this.#watchers.clear();
|
|
73
119
|
this.#pendingByCollection.clear();
|
|
74
120
|
this.#syncing.clear();
|
|
75
121
|
}
|
|
76
122
|
|
|
123
|
+
getState(): CollectionWatchState {
|
|
124
|
+
return {
|
|
125
|
+
expectedCollections: this.#collections.map(
|
|
126
|
+
(collection) => collection.name
|
|
127
|
+
),
|
|
128
|
+
activeCollections: [...this.#watchers.keys()],
|
|
129
|
+
failedCollections: [...this.#failedCollections.entries()].map(
|
|
130
|
+
([collection, reason]) => ({ collection, reason })
|
|
131
|
+
),
|
|
132
|
+
queuedCollections: [...this.#pendingByCollection.entries()]
|
|
133
|
+
.filter(([, relPaths]) => relPaths.size > 0)
|
|
134
|
+
.map(([collectionName]) => collectionName),
|
|
135
|
+
syncingCollections: [...this.#syncing],
|
|
136
|
+
lastEventAt: this.#lastEventAt,
|
|
137
|
+
lastSyncAt: this.#lastSyncAt,
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
|
|
77
141
|
#queueChange(collectionName: string, relPath: string): void {
|
|
78
142
|
const pending =
|
|
79
143
|
this.#pendingByCollection.get(collectionName) ?? new Set<string>();
|
|
@@ -132,6 +196,7 @@ export class CollectionWatchService {
|
|
|
132
196
|
return;
|
|
133
197
|
}
|
|
134
198
|
|
|
199
|
+
this.#lastSyncAt = new Date().toISOString();
|
|
135
200
|
this.#scheduler?.notifySyncComplete(relPaths);
|
|
136
201
|
|
|
137
202
|
for (const relPath of relPaths) {
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
declare module "electrobun" {
|
|
2
|
+
export interface ElectrobunConfig {
|
|
3
|
+
app: {
|
|
4
|
+
name: string;
|
|
5
|
+
identifier: string;
|
|
6
|
+
version: string;
|
|
7
|
+
urlSchemes?: string[];
|
|
8
|
+
};
|
|
9
|
+
runtime?: Record<string, unknown>;
|
|
10
|
+
build?: Record<string, unknown>;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
declare module "electrobun/bun" {
|
|
15
|
+
export const ApplicationMenu: {
|
|
16
|
+
setApplicationMenu(menu: unknown): void;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
export const BuildConfig: {
|
|
20
|
+
get(): Promise<{ runtime?: Record<string, unknown> }>;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
export class BrowserWindow {
|
|
24
|
+
constructor(options: unknown);
|
|
25
|
+
show(): void;
|
|
26
|
+
focus(): void;
|
|
27
|
+
webview: {
|
|
28
|
+
loadURL(url: string): void;
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export const GlobalShortcut: {
|
|
33
|
+
register(shortcut: string, handler: () => void): boolean;
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const Electrobun: {
|
|
37
|
+
events: {
|
|
38
|
+
on(name: string, handler: (event: unknown) => void): void;
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export default Electrobun;
|
|
43
|
+
}
|