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.
Files changed (71) hide show
  1. package/README.md +8 -37
  2. package/package.json +1 -2
  3. package/src/core/concurrency.ts +3 -6
  4. package/src/core/frontmatter.ts +0 -53
  5. package/src/core/graph/build.ts +6 -7
  6. package/src/core/graph/current.ts +2 -4
  7. package/src/core/graph/model.ts +1 -1
  8. package/src/core/graph/wikilinks.ts +3 -3
  9. package/src/core/index.ts +26 -27
  10. package/src/core/paths.ts +0 -7
  11. package/src/core/vault.ts +16 -681
  12. package/src/core/view/detail.ts +1 -1
  13. package/src/core/view/health.ts +1 -1
  14. package/src/core/view/tree.ts +1 -1
  15. package/src/pi/index.ts +6 -85
  16. package/src/pi/summarize.ts +2 -2
  17. package/src/pi/viewer/tui/bodyStore.ts +4 -7
  18. package/src/pi/viewer/tui/branding.ts +7 -148
  19. package/src/pi/viewer/tui/run.ts +3 -17
  20. package/src/pi/viewer/tui/surface/base.ts +24 -3
  21. package/src/pi/viewer/tui/surface/explore.ts +41 -6
  22. package/src/pi/viewer/tui/workspace.ts +23 -351
  23. package/src/pi/viewer/tui/workspaceRoot.ts +31 -172
  24. package/src/pi/viewer/web/run.ts +7 -117
  25. package/src/web/client/api.dom.ts +2 -2
  26. package/src/web/client/api.ts +14 -223
  27. package/src/web/client/bootstrap.ts +5 -14
  28. package/src/web/client/context/context.model.ts +9 -11
  29. package/src/web/client/dist/app.js +93 -219
  30. package/src/web/client/graph/dynamics.ts +5 -65
  31. package/src/web/client/graph/renderer.dom.ts +7 -8
  32. package/src/web/client/graph/renderer.ts +9 -35
  33. package/src/web/client/main.tsx +1 -1
  34. package/src/web/client/note/Note.tsx +21 -63
  35. package/src/web/client/search/SearchPalette.tsx +45 -36
  36. package/src/web/client/search/search.model.ts +33 -454
  37. package/src/web/client/shell/Columns.tsx +13 -83
  38. package/src/web/client/shell/Header.tsx +2 -10
  39. package/src/web/client/shell/Shell.tsx +50 -125
  40. package/src/web/client/shell/StatusBar.tsx +1 -4
  41. package/src/web/client/shell/icons.model.ts +4 -7
  42. package/src/web/client/shell/keys.model.ts +5 -42
  43. package/src/web/client/shell/keys.ts +2 -2
  44. package/src/web/client/shell/shell.model.ts +10 -133
  45. package/src/web/client/shell/theme.model.ts +2 -2
  46. package/src/web/client/shell/theme.ts +33 -157
  47. package/src/web/client/state.ts +9 -89
  48. package/src/web/client/tree/Tree.tsx +25 -575
  49. package/src/web/client/tree/tree.model.ts +8 -162
  50. package/src/web/client/workspace.ts +72 -242
  51. package/src/web/server/page.ts +8 -10
  52. package/src/web/server/routes.ts +30 -563
  53. package/src/web/server/server.ts +6 -145
  54. package/src/web/shared/layout.ts +72 -624
  55. package/src/web/shared/wire.ts +10 -196
  56. package/src/core/sessions.ts +0 -929
  57. package/src/pi/sessionScan.ts +0 -104
  58. package/src/pi/viewer/tui/explorer.ts +0 -586
  59. package/src/web/client/live.model.ts +0 -275
  60. package/src/web/client/live.ts +0 -151
  61. package/src/web/client/note/Editor.tsx +0 -109
  62. package/src/web/client/note/editor.controller.ts +0 -151
  63. package/src/web/client/note/editor.model.ts +0 -686
  64. package/src/web/client/search/search.ts +0 -107
  65. package/src/web/client/shell/Divider.tsx +0 -44
  66. package/src/web/client/shell/cssvars.ts +0 -70
  67. package/src/web/client/shell/drag.model.ts +0 -170
  68. package/src/web/client/shell/layout.model.ts +0 -500
  69. package/src/web/client/shell/viewport.ts +0 -29
  70. package/src/web/server/sse.ts +0 -321
  71. package/src/web/server/watcher.ts +0 -507
@@ -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 or an SSE frame is
12
- * written down, and both the server that produces it and the client that
13
- * consumes it are typed from it.
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 (§7.3). `null` when the server did not compute one —
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 the SSE dedupe
177
- * key (§5.3, §6).
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 SSE dedupe
188
- * dropped the frame that would have corrected it.
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
  }