pi-weave 0.1.12 → 0.1.13
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 +8 -37
- package/package.json +1 -2
- package/src/core/concurrency.ts +3 -6
- package/src/core/frontmatter.ts +0 -53
- package/src/core/graph/build.ts +6 -7
- package/src/core/graph/current.ts +2 -4
- package/src/core/graph/model.ts +1 -1
- package/src/core/graph/wikilinks.ts +3 -3
- package/src/core/index.ts +26 -27
- package/src/core/paths.ts +0 -7
- package/src/core/vault.ts +16 -681
- package/src/core/view/detail.ts +1 -1
- package/src/core/view/health.ts +1 -1
- package/src/core/view/tree.ts +1 -1
- package/src/pi/index.ts +6 -85
- package/src/pi/summarize.ts +2 -2
- package/src/pi/viewer/tui/bodyStore.ts +4 -7
- package/src/pi/viewer/tui/branding.ts +7 -148
- package/src/pi/viewer/tui/run.ts +3 -17
- package/src/pi/viewer/tui/surface/base.ts +24 -3
- package/src/pi/viewer/tui/surface/explore.ts +41 -6
- package/src/pi/viewer/tui/workspace.ts +23 -351
- package/src/pi/viewer/tui/workspaceRoot.ts +31 -172
- package/src/pi/viewer/web/run.ts +7 -117
- package/src/web/client/api.dom.ts +2 -2
- package/src/web/client/api.ts +14 -223
- package/src/web/client/bootstrap.ts +5 -14
- package/src/web/client/context/context.model.ts +9 -11
- package/src/web/client/dist/app.js +93 -219
- package/src/web/client/graph/dynamics.ts +5 -65
- package/src/web/client/graph/renderer.dom.ts +7 -8
- package/src/web/client/graph/renderer.ts +9 -35
- package/src/web/client/main.tsx +1 -1
- package/src/web/client/note/Note.tsx +21 -63
- package/src/web/client/search/SearchPalette.tsx +45 -36
- package/src/web/client/search/search.model.ts +33 -454
- package/src/web/client/shell/Columns.tsx +13 -83
- package/src/web/client/shell/Header.tsx +2 -10
- package/src/web/client/shell/Shell.tsx +50 -125
- package/src/web/client/shell/StatusBar.tsx +1 -4
- package/src/web/client/shell/icons.model.ts +4 -7
- package/src/web/client/shell/keys.model.ts +5 -42
- package/src/web/client/shell/keys.ts +2 -2
- package/src/web/client/shell/shell.model.ts +10 -133
- package/src/web/client/shell/theme.model.ts +2 -2
- package/src/web/client/shell/theme.ts +33 -157
- package/src/web/client/state.ts +9 -89
- package/src/web/client/tree/Tree.tsx +25 -575
- package/src/web/client/tree/tree.model.ts +8 -162
- package/src/web/client/workspace.ts +72 -242
- package/src/web/server/page.ts +8 -10
- package/src/web/server/routes.ts +30 -563
- package/src/web/server/server.ts +6 -145
- package/src/web/shared/layout.ts +72 -624
- package/src/web/shared/wire.ts +10 -196
- package/src/core/sessions.ts +0 -929
- package/src/pi/sessionScan.ts +0 -104
- package/src/pi/viewer/tui/explorer.ts +0 -586
- package/src/web/client/live.model.ts +0 -275
- package/src/web/client/live.ts +0 -151
- package/src/web/client/note/Editor.tsx +0 -109
- package/src/web/client/note/editor.controller.ts +0 -151
- package/src/web/client/note/editor.model.ts +0 -686
- package/src/web/client/search/search.ts +0 -107
- package/src/web/client/shell/Divider.tsx +0 -44
- package/src/web/client/shell/cssvars.ts +0 -70
- package/src/web/client/shell/drag.model.ts +0 -170
- package/src/web/client/shell/layout.model.ts +0 -500
- package/src/web/client/shell/viewport.ts +0 -29
- package/src/web/server/sse.ts +0 -321
- package/src/web/server/watcher.ts +0 -507
package/src/web/shared/wire.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* The wire contract between the loopback server and the browser client
|
|
3
|
-
* (weave-workspace §5.3).
|
|
3
|
+
* (weave-workspace §5.3, §10).
|
|
4
4
|
*
|
|
5
5
|
* ## Why this file exists at all
|
|
6
6
|
*
|
|
@@ -8,9 +8,9 @@
|
|
|
8
8
|
* Node-flavoured TypeScript and a value import would drag `node:fs` into the
|
|
9
9
|
* bundle. So everything both sides need is either a **type** here or a pure
|
|
10
10
|
* function in `src/web/shared/`. This module is the type half — it is the
|
|
11
|
-
* single place where the shape of an HTTP response
|
|
12
|
-
*
|
|
13
|
-
*
|
|
11
|
+
* single place where the shape of an HTTP response is written down, and both
|
|
12
|
+
* the server that produces it and the client that consumes it are typed from
|
|
13
|
+
* it.
|
|
14
14
|
*
|
|
15
15
|
* ## Tier rules
|
|
16
16
|
*
|
|
@@ -69,60 +69,6 @@ export type {
|
|
|
69
69
|
} from "./graph";
|
|
70
70
|
export { WIRE_EDGE_KINDS, WIRE_MODEL_OMITTED_KEYS, WIRE_NODE_KINDS } from "./graph";
|
|
71
71
|
|
|
72
|
-
// --- liveness ----------------------------------------------------------------
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Which half of the workspace an SSE frame is about.
|
|
76
|
-
*
|
|
77
|
-
* Deliberately coarser than the paths that produced it: macOS `fs.watch`
|
|
78
|
-
* coalesces and can miss rapid bursts, so a frame means "something in this
|
|
79
|
-
* scope changed, re-read it", never "here is the delta" (§6). A client that
|
|
80
|
-
* treated frames as deltas would silently diverge the first time the OS
|
|
81
|
-
* dropped one.
|
|
82
|
-
*/
|
|
83
|
-
export type ChangeScope = "vault" | "repo" | "git";
|
|
84
|
-
|
|
85
|
-
/** Every {@link ChangeScope}, for exhaustiveness checks and table tests. */
|
|
86
|
-
export const CHANGE_SCOPES: readonly ChangeScope[] = ["vault", "repo", "git"];
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* One SSE frame.
|
|
90
|
-
*
|
|
91
|
-
* `stamp` is the graph's content digest at the moment the change was observed
|
|
92
|
-
* — the same value `GET /api/graph` serves as its ETag (§5.3). It is the
|
|
93
|
-
* dedupe key: a client that already holds this stamp has nothing to refetch,
|
|
94
|
-
* which matters because the watcher's debounce window can still emit two
|
|
95
|
-
* frames for one logical edit.
|
|
96
|
-
*
|
|
97
|
-
* Sharing one key with the ETag is load-bearing rather than tidy. While this
|
|
98
|
-
* was `generatedAt`, an edit that did not advance the timestamp maximum
|
|
99
|
-
* produced a frame the client deduped away against the stamp of the graph it
|
|
100
|
-
* already held, so the refetch never happened (§15.6).
|
|
101
|
-
*/
|
|
102
|
-
export interface ChangeEvent {
|
|
103
|
-
scope: ChangeScope;
|
|
104
|
-
stamp: string;
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/** The `event:` name carried by every {@link ChangeEvent} frame. */
|
|
108
|
-
export const CHANGE_EVENT_NAME = "change";
|
|
109
|
-
|
|
110
|
-
/**
|
|
111
|
-
* Structural guard for a decoded SSE `data:` payload.
|
|
112
|
-
*
|
|
113
|
-
* The client parses frames off a socket that survives server restarts and
|
|
114
|
-
* proxy interference, so "it is JSON" is not the same as "it is ours".
|
|
115
|
-
* Narrow rather than validate-and-throw: a malformed frame should cost a
|
|
116
|
-
* skipped refetch, not an unhandled rejection inside `EventSource`'s
|
|
117
|
-
* callback.
|
|
118
|
-
*/
|
|
119
|
-
export function isChangeEvent(value: unknown): value is ChangeEvent {
|
|
120
|
-
if (typeof value !== "object" || value === null) return false;
|
|
121
|
-
const candidate = value as { scope?: unknown; stamp?: unknown };
|
|
122
|
-
if (typeof candidate.stamp !== "string") return false;
|
|
123
|
-
return CHANGE_SCOPES.includes(candidate.scope as ChangeScope);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
72
|
// --- responses ---------------------------------------------------------------
|
|
127
73
|
|
|
128
74
|
/**
|
|
@@ -166,15 +112,15 @@ export interface GraphPayload {
|
|
|
166
112
|
dangling: Record<string, string[]>;
|
|
167
113
|
/**
|
|
168
114
|
* Server-precomputed layout, so the graph appears already laid out with no
|
|
169
|
-
* visible settling (§
|
|
115
|
+
* visible settling (§8). `null` when the server did not compute one —
|
|
170
116
|
* which is the default, because the layout module imports `d3-force` and
|
|
171
117
|
* the published package has zero runtime dependencies. The client then
|
|
172
118
|
* runs the identical `src/web/shared/layout` code itself.
|
|
173
119
|
*/
|
|
174
120
|
positions: Record<string, Point> | null;
|
|
175
121
|
/**
|
|
176
|
-
* A **content digest** of this payload. The ETag body and
|
|
177
|
-
*
|
|
122
|
+
* A **content digest** of this payload. The ETag body and polling cache key
|
|
123
|
+
* (§15.6, §7.3).
|
|
178
124
|
*
|
|
179
125
|
* Opaque: a truncated SHA-256 of the serialized payload, and nothing may
|
|
180
126
|
* parse it or derive meaning from its value. The only defined operation is
|
|
@@ -184,8 +130,8 @@ export interface GraphPayload {
|
|
|
184
130
|
* This was `model.generatedAt` until §15.6. A max of input timestamps is
|
|
185
131
|
* blind to any change that does not advance the maximum (a body edit, a
|
|
186
132
|
* front-matter edit, deleting a note that is not the newest), so a
|
|
187
|
-
* conditional GET answered `304` with stale content and the
|
|
188
|
-
*
|
|
133
|
+
* conditional GET answered `304` with stale content and the client cache
|
|
134
|
+
* kept the stale payload.
|
|
189
135
|
*
|
|
190
136
|
* For a human-readable "data as of" marker, read {@link GraphModel.generatedAt},
|
|
191
137
|
* which still carries it — the status bar does exactly that.
|
|
@@ -193,134 +139,11 @@ export interface GraphPayload {
|
|
|
193
139
|
stamp: string;
|
|
194
140
|
}
|
|
195
141
|
|
|
196
|
-
/**
|
|
197
|
-
* `GET /api/note/:slug`, and the note half of every write response.
|
|
198
|
-
*
|
|
199
|
-
* The route used to serve a bare {@link ViewNote}. It carries a `revision`
|
|
200
|
-
* as of P5 because the editor cannot save safely without one: a save that
|
|
201
|
-
* does not say *which* state it was editing is a last-write-wins save, and
|
|
202
|
-
* the two writers a vault actually has — `$EDITOR` and an agent's
|
|
203
|
-
* `weave_note` call — are both fast enough to lose a paragraph to it.
|
|
204
|
-
*
|
|
205
|
-
* Carried in the **body** rather than as an `ETag`, deliberately. `api.ts`'s
|
|
206
|
-
* {@link HttpResponse} port exposes `ok`, `status` and `json()` and nothing
|
|
207
|
-
* else, because it exists so a two-line fake can stand in for `fetch` in a
|
|
208
|
-
* repository with no DOM (§10). Reading a header would mean widening that
|
|
209
|
-
* port for one field, and the field is a *property of the note*, not of the
|
|
210
|
-
* HTTP representation — unlike `/api/graph`'s stamp, which really is a cache
|
|
211
|
-
* validator and really does belong in an `ETag`.
|
|
212
|
-
*/
|
|
142
|
+
/** `GET /api/note/:slug`. */
|
|
213
143
|
export interface NotePayload {
|
|
214
144
|
note: ViewNote;
|
|
215
|
-
/**
|
|
216
|
-
* Opaque version stamp for the file the note was read from.
|
|
217
|
-
*
|
|
218
|
-
* **Compare it, do not interpret it.** Core currently derives it from
|
|
219
|
-
* `mtimeMs:size` and says in the same breath that the shape is not part of
|
|
220
|
-
* the contract; a client that parsed a timestamp out of it would break the
|
|
221
|
-
* day core upgrades it to a digest. The only defined operation is equality
|
|
222
|
-
* against a later read of the same note.
|
|
223
|
-
*/
|
|
224
|
-
revision: string;
|
|
225
145
|
}
|
|
226
146
|
|
|
227
|
-
/**
|
|
228
|
-
* `POST /api/note/:slug` request body.
|
|
229
|
-
*
|
|
230
|
-
* Every field optional, and that is not laziness — it mirrors core's
|
|
231
|
-
* `UpdateNoteInput`, where a metadata-only edit (retagging) and a body-only
|
|
232
|
-
* edit (the textarea) are both first-class. A body of `{}` is a legal
|
|
233
|
-
* request that bumps `updated` and nothing else.
|
|
234
|
-
*/
|
|
235
|
-
export interface SaveNoteRequest {
|
|
236
|
-
/** Replacement Markdown body. Omit to change only metadata. */
|
|
237
|
-
body?: string;
|
|
238
|
-
/**
|
|
239
|
-
* Metadata to merge over the note's current values.
|
|
240
|
-
*
|
|
241
|
-
* `created` is absent by construction: it records when the note came into
|
|
242
|
-
* existence and an edit is not a re-creation. `updated` is absent because
|
|
243
|
-
* the server owns it — a client that could set it could make an edit look
|
|
244
|
-
* older than the state it overwrote.
|
|
245
|
-
*/
|
|
246
|
-
meta?: {
|
|
247
|
-
title?: string;
|
|
248
|
-
tags?: string[];
|
|
249
|
-
source?: WireNoteSource;
|
|
250
|
-
};
|
|
251
|
-
/**
|
|
252
|
-
* The {@link NotePayload.revision} the client last read.
|
|
253
|
-
*
|
|
254
|
-
* Supply it and a stale save is refused with `409` and a
|
|
255
|
-
* {@link ConflictPayload} carrying the note as it now is. Omit it and the
|
|
256
|
-
* save is last-write-wins — which is a legitimate thing for the *user* to
|
|
257
|
-
* choose after seeing a conflict, and is exactly how "overwrite" is
|
|
258
|
-
* expressed: the same request, resent without this field.
|
|
259
|
-
*/
|
|
260
|
-
expectedRevision?: string;
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
/** `POST /api/note/:slug/rename` request body. */
|
|
264
|
-
export interface RenameNoteRequest {
|
|
265
|
-
/**
|
|
266
|
-
* The destination. Passed through core's `slugify`, so a human title is
|
|
267
|
-
* as acceptable as a slug and both land on the same file name.
|
|
268
|
-
*/
|
|
269
|
-
slug: string;
|
|
270
|
-
title?: string;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
|
-
/** `POST /api/note/:slug/move` request body. */
|
|
274
|
-
export interface MoveNoteRequest {
|
|
275
|
-
/** Target folder path relative to notes/, or null/empty/"vault" for root. */
|
|
276
|
-
readonly targetFolder: string | null;
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
/** `POST /api/folder` request body. */
|
|
280
|
-
export interface CreateFolderRequest {
|
|
281
|
-
readonly path: string;
|
|
282
|
-
}
|
|
283
|
-
|
|
284
|
-
/** `POST /api/folder` response. */
|
|
285
|
-
export interface CreateFolderResult {
|
|
286
|
-
readonly ok: true;
|
|
287
|
-
readonly path: string;
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
/** `DELETE /api/note/:slug` response. Hard delete — there is no trash. */
|
|
291
|
-
export interface DeleteNoteResult {
|
|
292
|
-
deleted: true;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
/**
|
|
296
|
-
* The `409` body for both write routes.
|
|
297
|
-
*
|
|
298
|
-
* A discriminated union rather than one shape with optional halves, because
|
|
299
|
-
* the two cases carry genuinely different payloads and a client that has to
|
|
300
|
-
* check `current !== undefined` before it can tell them apart is a client
|
|
301
|
-
* that will one day forget to.
|
|
302
|
-
*
|
|
303
|
-
* The `conflict` arm carries the **whole current note**, not just its
|
|
304
|
-
* revision. That is the difference between a UI that can offer
|
|
305
|
-
* reload-or-overwrite immediately and one that has to issue a second request
|
|
306
|
-
* before it can say anything useful — and the second request is one more
|
|
307
|
-
* window in which the file moves again. Core hands the server this note as
|
|
308
|
-
* part of the failure, so shipping it costs nothing.
|
|
309
|
-
*/
|
|
310
|
-
export type ConflictPayload =
|
|
311
|
-
| {
|
|
312
|
-
error: string;
|
|
313
|
-
reason: "conflict";
|
|
314
|
-
/** The note as it is on disk right now, with its current revision. */
|
|
315
|
-
current: NotePayload;
|
|
316
|
-
}
|
|
317
|
-
| {
|
|
318
|
-
error: string;
|
|
319
|
-
reason: "collision";
|
|
320
|
-
/** The destination slug that is already taken. */
|
|
321
|
-
slug: string;
|
|
322
|
-
};
|
|
323
|
-
|
|
324
147
|
/** `GET /api/okf/:rel`. */
|
|
325
148
|
export interface OkfFilePayload {
|
|
326
149
|
/** The requested path, relative to `<cwd>/.okf`. Echoed, never resolved. */
|
|
@@ -364,13 +187,4 @@ export const BOOTSTRAP_ELEMENT_ID = "weave-bootstrap";
|
|
|
364
187
|
export interface Bootstrap {
|
|
365
188
|
/** Absolute path the workspace was started in. */
|
|
366
189
|
cwd: string;
|
|
367
|
-
/** Absolute path of the vault root. */
|
|
368
|
-
vaultRoot: string;
|
|
369
|
-
/**
|
|
370
|
-
* Random per-boot id. `EventSource` reconnects transparently across a
|
|
371
|
-
* server restart, so without this a client cannot distinguish two cases it
|
|
372
|
-
* must handle differently: "I missed some frames" (refetch) and "this is a
|
|
373
|
-
* different server" (full reload).
|
|
374
|
-
*/
|
|
375
|
-
session: string;
|
|
376
190
|
}
|