pi-weave 0.3.8 → 0.3.10

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 (80) hide show
  1. package/README.md +21 -17
  2. package/package.json +4 -3
  3. package/skills/weave-explore/SKILL.md +3 -5
  4. package/skills/weave-notepad/SKILL.md +22 -2
  5. package/skills/weave-notepad/references/second-thoughts.md +57 -0
  6. package/src/core/graph/build.ts +1 -1
  7. package/src/core/graph/current.ts +10 -8
  8. package/src/core/history.ts +417 -0
  9. package/src/core/index.ts +19 -0
  10. package/src/core/links/repair.ts +5 -68
  11. package/src/core/noteAction.ts +72 -2
  12. package/src/core/openInEditor.ts +3 -8
  13. package/src/core/repoAction.ts +20 -61
  14. package/src/core/repoIndex.ts +38 -0
  15. package/src/core/trail.ts +124 -0
  16. package/src/core/types.ts +1 -3
  17. package/src/core/vault.ts +116 -29
  18. package/src/core/view/focus.ts +2 -2
  19. package/src/core/view/index.ts +2 -3
  20. package/src/core/view/rawTail.ts +153 -0
  21. package/src/core/workspace.ts +20 -26
  22. package/src/core/workspaceCommands.ts +14 -14
  23. package/src/opencode/tools.ts +3 -3
  24. package/src/opencode/tui.ts +1 -7
  25. package/src/pi/index.ts +68 -80
  26. package/src/pi/tools/noteTool.ts +1 -1
  27. package/src/pi/tools/repoTool.ts +3 -6
  28. package/src/pi/viewer/web/run.ts +6 -8
  29. package/src/web/client/api.ts +103 -5
  30. package/src/web/client/context/context.model.ts +5 -0
  31. package/src/web/client/dist/app.js +499 -129
  32. package/src/web/client/graph/Graph.tsx +166 -14
  33. package/src/web/client/graph/column.model.ts +59 -2
  34. package/src/web/client/graph/graph.model.ts +183 -7
  35. package/src/web/client/graph/renderer.ts +39 -9
  36. package/src/web/client/graph/spotlight.model.ts +472 -0
  37. package/src/web/client/note/Note.tsx +370 -66
  38. package/src/web/client/note/Replay.tsx +139 -0
  39. package/src/web/client/note/drafts.ts +115 -11
  40. package/src/web/client/note/editbar.model.ts +153 -0
  41. package/src/web/client/note/note.model.ts +82 -1
  42. package/src/web/client/note/replay.model.ts +248 -0
  43. package/src/web/client/search/SearchPalette.tsx +103 -34
  44. package/src/web/client/search/search.model.ts +533 -26
  45. package/src/web/client/shell/ContextRail.tsx +7 -0
  46. package/src/web/client/shell/Settings.tsx +21 -1
  47. package/src/web/client/shell/Shell.tsx +93 -27
  48. package/src/web/client/shell/StatusBar.tsx +1 -0
  49. package/src/web/client/shell/icons.model.ts +68 -1
  50. package/src/web/client/shell/keys.model.ts +31 -8
  51. package/src/web/client/shell/shell.model.ts +48 -1
  52. package/src/web/client/shell/theme.model.ts +3 -0
  53. package/src/web/client/shell/theme.ts +354 -32
  54. package/src/web/client/shell/workspace.css.ts +40 -9
  55. package/src/web/client/trail/TrailStrip.tsx +55 -0
  56. package/src/web/client/trail/trail.model.ts +195 -0
  57. package/src/web/client/trail/trail.ts +45 -0
  58. package/src/web/client/tree/Tree.tsx +7 -3
  59. package/src/web/client/tree/tree.model.ts +2 -5
  60. package/src/web/server/page.ts +1 -1
  61. package/src/web/server/routes.ts +83 -3
  62. package/src/web/shared/logo.ts +1 -1
  63. package/src/web/shared/themes.ts +1 -1
  64. package/src/web/shared/view.ts +10 -4
  65. package/src/web/shared/wire.ts +74 -1
  66. package/src/web/shared/workspace.ts +10 -2
  67. package/src/core/view/health.ts +0 -160
  68. package/src/pi/viewer/tui/bodyStore.ts +0 -95
  69. package/src/pi/viewer/tui/branding.ts +0 -14
  70. package/src/pi/viewer/tui/model.ts +0 -390
  71. package/src/pi/viewer/tui/openNote.ts +0 -11
  72. package/src/pi/viewer/tui/run.ts +0 -71
  73. package/src/pi/viewer/tui/surface/base.ts +0 -203
  74. package/src/pi/viewer/tui/surface/detail.ts +0 -315
  75. package/src/pi/viewer/tui/surface/explore.ts +0 -241
  76. package/src/pi/viewer/tui/surface/focus.ts +0 -139
  77. package/src/pi/viewer/tui/surface/health.ts +0 -102
  78. package/src/pi/viewer/tui/theme.ts +0 -90
  79. package/src/pi/viewer/tui/workspace.ts +0 -53
  80. package/src/pi/viewer/tui/workspaceRoot.ts +0 -389
@@ -67,8 +67,9 @@ export const THEME_CSS = `
67
67
  --weave-note-gutter:18px;
68
68
  /* The desk-and-page split. The workspace is a desk of instruments — tree,
69
69
  graph, rail, bars — on the --weave-bg ground; the note column is the one
70
- *page* lying on it: crust in both Catppuccin schemes, the ground that
71
- sits furthest from the desk (deep under dark, bright over light). Two
70
+ *page* lying on it: crust under dark, plain white under Latte (the
71
+ refresh's "quiet page": paper reads as paper), the ground that sits
72
+ furthest from the desk. Two
72
73
  voices in total: sans for instruments and prose alike, mono for data — a
73
74
  third face was tried (serif prose) and withdrawn at the user's call; two
74
75
  voices read calmer than three. The CSP allows nothing fetched, and both
@@ -79,7 +80,7 @@ export const THEME_CSS = `
79
80
  two overlays that float above the grid. Tag pills are 999px — a capsule
80
81
  shape, not a scale step. The gate test below refuses any literal px
81
82
  radius so the scale cannot drift back. */
82
- --weave-radius:4px;--weave-radius-pop:7px;
83
+ --weave-radius:6px;--weave-radius-pop:10px;
83
84
  /* The type ramp, role-named because sizes drift and roles stick. Nine
84
85
  steps; every font-size in this sheet is one of these vars — that is a
85
86
  gate, not a convention:
@@ -91,7 +92,7 @@ export const THEME_CSS = `
91
92
  body 13.5 the note column's prose
92
93
  subhead 14 palette input, note-body h2, ribbon icon buttons
93
94
  title 15 note-body h1
94
- display 20 the note's page title
95
+ display 26 the note's page title (20 before the refresh)
95
96
  Two earlier sizes were merged into neighbours: 9px glyphs to prov, and
96
97
  the 10px column overlines to caption (caps plus .09em tracking already
97
98
  read larger than their point size suggests). \`display\` and \`body\` are
@@ -106,7 +107,7 @@ export const THEME_CSS = `
106
107
  rubber stamp. */
107
108
  --weave-px-prov:9.5px;--weave-px-caption:10.5px;--weave-px-ui:11.5px;
108
109
  --weave-px-row:12px;--weave-px-base:13px;--weave-px-body:13.5px;
109
- --weave-px-subhead:14px;--weave-px-title:15px;--weave-px-display:20px;
110
+ --weave-px-subhead:14px;--weave-px-title:15px;--weave-px-display:26px;
110
111
  }
111
112
  @media (prefers-color-scheme: light){
112
113
  :root:not([data-weave-theme]){${paletteTokens("light")}}
@@ -166,7 +167,7 @@ body{font-size:var(--weave-px-base)}
166
167
  so the selected row is always readable. */
167
168
  .weave-row-on .weave-twisty,.weave-row-on .weave-kind,.weave-row-on .weave-prov,
168
169
  .weave-row-on .weave-meta,.weave-row-on .weave-label{color:var(--weave-fg)}
169
- .weave-hit-on .weave-hit-badge,.weave-hit-on .weave-hit-detail{color:var(--weave-fg)}
170
+ .weave-hit-on .weave-hit-icon,.weave-hit-on .weave-hit-detail,.weave-hit-on .weave-hit-meta{color:var(--weave-fg)}
170
171
  /* A newly-arrived node (a file or note added since the last update, ):
171
172
  one short highlight that fades while the label settles from bold back to
172
173
  normal. The class is computed from the frame diff in workspace.ts and
@@ -178,6 +179,16 @@ body{font-size:var(--weave-px-base)}
178
179
  40%{font-weight:700}
179
180
  100%{background:transparent;font-weight:400}
180
181
  }
182
+ /* Recall trail (issue #78): a note the agent searched, read or wrote. The
183
+ tint follows the graph's palette for the same kinds (warn → ok → accent)
184
+ and steps down through three strength buckets as the glow fades. */
185
+ .weave-row-trail{--weave-trail:var(--weave-warn);box-shadow:inset 2px 0 0 color-mix(in srgb,var(--weave-trail) var(--weave-trail-mix),transparent);background:color-mix(in srgb,var(--weave-trail) calc(var(--weave-trail-mix) / 6),transparent)}
186
+ .weave-row-trail-read{--weave-trail:var(--weave-ok)}
187
+ .weave-row-trail-wrote{--weave-trail:var(--weave-accent)}
188
+ .weave-row-trail-3{--weave-trail-mix:90%}
189
+ .weave-row-trail-2{--weave-trail-mix:55%}
190
+ .weave-row-trail-1{--weave-trail-mix:25%}
191
+ .weave-row-trail-wrote.weave-row-trail-3 .weave-label{font-weight:600}
181
192
  .weave-twisty{width:16px;height:16px;flex:none;display:inline-flex;align-items:center;justify-content:center;color:var(--weave-faint);cursor:pointer}
182
193
  .weave-kind{flex:none;color:var(--weave-faint);font-size:var(--weave-px-ui)}
183
194
  .weave-prov{flex:none;font-size:var(--weave-px-prov)}
@@ -235,7 +246,7 @@ body{font-size:var(--weave-px-base)}
235
246
  .weave-note-head{position:sticky;top:0;z-index:2;padding:14px var(--weave-note-gutter) 9px;background:var(--weave-page);border-bottom:1px solid var(--weave-line)}
236
247
  /* The page's largest voice, and the point of the review's headline finding:
237
248
  a 15px title sat 1.5px above the body it named. --weave-px-display puts the
238
- title where a page's title belongs, weight 650 for presence at 20px (600
249
+ title where a page's title belongs, weight 650 for presence at 26px (600
239
250
  reads thin at that size, 700 reads as a poster), tracking pulled in by a
240
251
  hair because large sans needs it. */
241
252
  .weave-note-title{margin:0 0 4px;font-size:var(--weave-px-display);font-weight:650;line-height:1.25;letter-spacing:-.005em;color:var(--weave-fg)}
@@ -252,25 +263,164 @@ body{font-size:var(--weave-px-base)}
252
263
  }
253
264
  .weave-note-open:hover{color:var(--weave-fg);background:var(--weave-line)}
254
265
  .weave-note-open-mark{display:inline-flex;line-height:0}
255
- /* Save / Done, chip-shaped: the same quiet row, and a control in a reading
256
- column should not outweigh the title. */
257
- .weave-note-edit,.weave-note-save{
266
+ /* The note's own actions, at the end of the meta line. History is a quiet
267
+ word with an icon; Edit is the one bordered control, and neither outweighs
268
+ the title. */
269
+ .weave-note-actions{margin-left:auto;display:inline-flex;align-items:center;gap:4px}
270
+ .weave-note-meta .weave-note-action{
271
+ display:inline-flex;align-items:center;gap:5px;
258
272
  font:inherit;font-size:var(--weave-px-caption);line-height:1;white-space:nowrap;cursor:pointer;
259
- color:var(--weave-dim);background:var(--weave-panel);padding:4px 7px;
260
- border:1px solid var(--weave-line-strong);border-radius:var(--weave-radius);
273
+ color:var(--weave-dim);background:none;padding:5px 8px;
274
+ border:1px solid transparent;border-radius:var(--weave-radius);
275
+ }
276
+ .weave-note-meta .weave-note-action .weave-icon{width:13px;height:13px}
277
+ .weave-note-meta .weave-note-action:hover{color:var(--weave-fg);background:var(--weave-new)}
278
+ .weave-note-meta .weave-note-action:disabled{color:var(--weave-faint);background:none;cursor:default}
279
+ .weave-note-meta .weave-note-edit{color:var(--weave-fg);background:var(--weave-page);border-color:var(--weave-line-strong)}
280
+ .weave-note-meta .weave-note-edit:hover{border-color:var(--weave-accent);background:var(--weave-page)}
281
+ .weave-note-meta .weave-note-history[aria-pressed="true"]{color:var(--weave-accent);background:var(--weave-new)}
282
+ /* Where the note lives, above its title: a crumb, not a heading. */
283
+ .weave-note-crumb{margin:0 0 6px;display:flex;align-items:center;gap:5px;font-size:var(--weave-px-caption);color:var(--weave-faint)}
284
+ .weave-note-crumb .weave-icon{width:12px;height:12px}
285
+ /* Who wrote it, in words, with the spine's colour as a dot. */
286
+ .weave-note-meta .weave-prov{display:inline-flex;align-items:center;gap:6px;font-size:var(--weave-px-ui);color:var(--weave-dim)}
287
+ .weave-prov-dot{width:7px;height:7px;border-radius:999px;background:var(--weave-spine,var(--weave-accent))}
288
+ /* The edit bar (E1): one row while editing, pinned over the text it edits.
289
+ Formatting on the left, where the words are; the save state and the two
290
+ ways out on the right. Saving is automatic, so Done is the only
291
+ affirmative control and the only one in the accent. */
292
+ .weave-editbar{
293
+ position:sticky;top:0;z-index:3;display:flex;align-items:center;gap:6px;flex-wrap:wrap;
294
+ min-height:40px;padding:5px 12px;font-size:var(--weave-px-ui);
295
+ background:var(--weave-page);border-bottom:1px solid var(--weave-line);
296
+ }
297
+ .weave-editbar .weave-icon{width:14px;height:14px}
298
+ .weave-editbar-badge{
299
+ display:inline-flex;align-items:center;gap:5px;padding:3px 9px;border-radius:999px;
300
+ font-size:var(--weave-px-caption);font-weight:600;color:var(--weave-accent);background:var(--weave-new);
301
+ }
302
+ .weave-editbar-badge .weave-icon{width:12px;height:12px}
303
+ .weave-editbar-tools{display:inline-flex;align-items:center;gap:1px;margin-left:4px;padding-left:8px;border-left:1px solid var(--weave-line)}
304
+ .weave-editbar .weave-editbar-tool{
305
+ display:inline-grid;place-items:center;width:28px;height:28px;padding:0;cursor:pointer;
306
+ color:var(--weave-dim);background:none;border:0;border-radius:var(--weave-radius);
307
+ }
308
+ .weave-editbar .weave-editbar-tool:hover{color:var(--weave-fg);background:var(--weave-new)}
309
+ .weave-editbar-status{margin-left:auto;display:inline-flex;align-items:center;gap:5px;white-space:nowrap;font-size:var(--weave-px-caption);color:var(--weave-faint)}
310
+ .weave-editbar-pending,.weave-editbar-saving{color:var(--weave-dim)}
311
+ .weave-editbar-saved{color:var(--weave-ok)}
312
+ .weave-editbar-failed{color:var(--weave-bad);font-weight:600}
313
+ .weave-editbar .weave-editbar-retry{font:inherit;color:var(--weave-accent);background:none;border:0;padding:2px 4px;cursor:pointer;text-decoration:underline;text-underline-offset:2px}
314
+ .weave-editbar .weave-editbar-discard,.weave-editbar .weave-editbar-done{font:inherit;font-size:var(--weave-px-caption);line-height:1;white-space:nowrap;cursor:pointer;border-radius:var(--weave-radius)}
315
+ .weave-editbar .weave-editbar-discard{padding:7px 9px;color:var(--weave-dim);background:none;border:1px solid transparent}
316
+ .weave-editbar .weave-editbar-discard:hover{color:var(--weave-fg);background:var(--weave-new)}
317
+ .weave-editbar .weave-editbar-done{padding:7px 14px;font-weight:600;color:var(--weave-page);background:var(--weave-accent);border:1px solid var(--weave-accent)}
318
+ .weave-editbar .weave-editbar-done:hover{filter:brightness(1.08)}
319
+ .weave-editbar .weave-editbar-discard:disabled,.weave-editbar .weave-editbar-done:disabled{opacity:.5;cursor:default;filter:none}
320
+ /* A finalized note's opening TLDR, set apart as the first thing to read.
321
+ The label is the card's own; task boxes keep their row and lose the bullet. */
322
+ .weave-tldr{margin:0 0 18px;padding:12px 16px;background:var(--weave-new);border:1px solid var(--weave-line-strong);border-radius:var(--weave-radius)}
323
+ .weave-tldr>p,.weave-tldr>ul,.weave-tldr>ol{margin:0 0 6px}
324
+ .weave-tldr>*:last-child{margin-bottom:0}
325
+ .weave-tldr ul:has(>li>input[type="checkbox"]){list-style:none;padding-left:2px}
326
+ .weave-tldr-label{display:block;margin-bottom:3px;font-size:var(--weave-px-prov);font-weight:700;letter-spacing:.08em;color:var(--weave-accent)}
327
+ /* The replay band sits under the header like the header itself: full width,
328
+ the note's gutter, one line of words, one line of controls. Dots on the
329
+ track are the versions, coloured by who made them. */
330
+ .weave-replay{
331
+ display:flex;flex-direction:column;gap:8px;padding:10px var(--weave-note-gutter) 12px;
332
+ background:var(--weave-panel);border-bottom:1px solid var(--weave-line);
333
+ font-size:var(--weave-px-ui);color:var(--weave-dim);
334
+ }
335
+ .weave-replay-note{color:var(--weave-faint)}
336
+ .weave-replay-caption{margin:0;display:flex;align-items:center;gap:10px;flex-wrap:wrap;min-height:18px}
337
+ .weave-replay-clock{display:inline-flex;color:var(--weave-dim)}
338
+ .weave-replay-position{font-weight:650;color:var(--weave-fg);font-variant-numeric:tabular-nums}
339
+ .weave-replay-who{display:inline-flex;align-items:center;gap:6px;color:var(--weave-dim)}
340
+ .weave-replay-who::before{content:"";width:8px;height:8px;border-radius:999px;background:var(--weave-who)}
341
+ .weave-replay-time{color:var(--weave-faint)}
342
+ .weave-replay-change{color:var(--weave-ok);font-size:var(--weave-px-caption);font-variant-numeric:tabular-nums}
343
+ .weave-replay-human{--weave-who:var(--weave-ok)}
344
+ .weave-replay-agent{--weave-who:var(--weave-accent)}
345
+ .weave-replay-outside,.weave-replay-system{--weave-who:var(--weave-faint)}
346
+ .weave-replay-controls{display:flex;align-items:center;gap:6px}
347
+ .weave-replay-controls button{
348
+ flex:none;display:inline-grid;place-items:center;width:28px;height:28px;padding:0;cursor:pointer;
349
+ color:var(--weave-fg);background:var(--weave-page);
350
+ border:1px solid var(--weave-line-strong);border-radius:var(--weave-radius-pop);
261
351
  }
262
- .weave-note-edit:hover,.weave-note-save:hover{color:var(--weave-fg);border-color:var(--weave-accent)}
263
- /* Save is the row's only affirmative action, so the only one with the accent. */
264
- .weave-note-save{color:var(--weave-accent);border-color:var(--weave-accent)}
265
- .weave-note-save:disabled{color:var(--weave-faint);border-color:var(--weave-line-strong);cursor:default}
352
+ .weave-replay-controls button:hover:not(:disabled){border-color:var(--weave-accent)}
353
+ .weave-replay-controls button:focus-visible{outline:2px solid var(--weave-accent);outline-offset:1px}
354
+ .weave-replay-controls button:disabled{color:var(--weave-faint);cursor:default;opacity:.6}
355
+ .weave-replay-controls .weave-replay-play{color:var(--weave-page);background:var(--weave-accent);border-color:var(--weave-accent)}
356
+ .weave-replay-controls .weave-replay-play:hover:not(:disabled){filter:brightness(1.08)}
357
+ /* The change bars (the refresh's H2): one bar per version, as tall as the
358
+ change was big, coloured by who made it; the version on screen is ringed.
359
+ The day labels sit under the band. Heights and label positions arrive as
360
+ custom properties through Preact's CSSOM style path, never an attribute. */
361
+ .weave-replay-track{--weave-replay-at:100%;position:relative;flex:1;min-width:80px;height:46px;margin-left:8px}
362
+ .weave-replay-track::before{
363
+ content:"";position:absolute;left:7px;right:7px;top:31px;height:2px;border-radius:999px;
364
+ background:linear-gradient(to right,color-mix(in srgb,var(--weave-accent) 55%,transparent) var(--weave-replay-at),var(--weave-line-strong) var(--weave-replay-at));
365
+ }
366
+ .weave-replay-bars{position:absolute;left:7px;right:7px;top:2px;height:26px;pointer-events:none}
367
+ .weave-replay-bar{position:absolute;bottom:0;left:var(--weave-bar-at,0);width:max(1px,min(8px,calc(100% / var(--weave-bars,1) - 2px)));transform:translateX(-50%);height:var(--weave-bar,14%);border-radius:var(--weave-radius);background:var(--weave-who);opacity:.85}
368
+ .weave-replay-ahead{opacity:.35}
369
+ .weave-replay-current{opacity:1;outline:2px solid var(--weave-accent);outline-offset:1px}
370
+ .weave-replay-days{position:absolute;left:7px;right:7px;top:34px;height:12px;pointer-events:none}
371
+ .weave-replay-day{position:absolute;left:var(--weave-day-at,0);transform:translateX(-2px);font-size:var(--weave-px-prov);line-height:12px;color:var(--weave-faint);white-space:nowrap}
372
+ .weave-replay-slider{position:absolute;inset:0;width:100%;height:100%;margin:0;background:transparent;-webkit-appearance:none;appearance:none;cursor:pointer}
373
+ .weave-replay-slider:disabled{cursor:default}
374
+ .weave-replay-slider:focus{outline:none}
375
+ .weave-replay-slider::-webkit-slider-runnable-track{height:46px;background:transparent}
376
+ .weave-replay-slider::-moz-range-track{height:46px;background:transparent}
377
+ .weave-replay-slider::-webkit-slider-thumb{
378
+ -webkit-appearance:none;appearance:none;width:14px;height:14px;margin-top:25px;border-radius:999px;
379
+ background:var(--weave-page);border:2px solid var(--weave-accent);box-sizing:border-box;
380
+ }
381
+ .weave-replay-slider::-moz-range-thumb{
382
+ width:14px;height:14px;border-radius:999px;box-sizing:border-box;
383
+ background:var(--weave-page);border:2px solid var(--weave-accent);
384
+ }
385
+ /* Focus fills the thumb: a ring would need an elevation shadow. */
386
+ .weave-replay-slider:focus-visible::-webkit-slider-thumb{background:var(--weave-accent);border-color:var(--weave-page)}
387
+ .weave-replay-slider:focus-visible::-moz-range-thumb{background:var(--weave-accent);border-color:var(--weave-page)}
388
+ .weave-replay-hint{margin:0;color:var(--weave-faint)}
389
+ .weave-replay-mark{background:var(--weave-new);color:inherit}
266
390
  /* Mono and a tighter line than the prose: this is Markdown source, where
267
391
  alignment carries meaning proportional text throws away. */
268
392
  .weave-note-editor{
269
- display:block;width:100%;flex:1;min-height:0;resize:none;
393
+ display:block;width:100%;flex:1 0 auto;min-height:12em;resize:none;
270
394
  padding:12px var(--weave-note-gutter) 28px;box-sizing:border-box;
271
395
  font-family:var(--weave-mono);font-size:var(--weave-px-row);line-height:1.6;
272
- color:var(--weave-fg);background:var(--weave-bg);border:0;outline:0;
396
+ color:var(--weave-fg);background:var(--weave-page);border:0;outline:0;
397
+ }
398
+ /* The \`## Raw\` box under the editor: folded by default so a long
399
+ dictation never outgrows the note, a dashed edge to say "captured, not
400
+ written", and a capped height once open. */
401
+ .weave-raw{
402
+ flex:none;margin:0 var(--weave-note-gutter) 24px;
403
+ border:1px dashed var(--weave-line-strong);border-radius:var(--weave-radius-pop);background:var(--weave-panel);
404
+ }
405
+ .weave-raw-head{display:flex;align-items:center;gap:10px;padding:5px 8px;min-height:24px}
406
+ .weave-raw[data-open] .weave-raw-head{border-bottom:1px dashed var(--weave-line-strong)}
407
+ .weave-raw-toggle{
408
+ display:inline-flex;align-items:center;gap:6px;padding:3px 6px;cursor:pointer;
409
+ font:inherit;font-size:var(--weave-px-ui);font-weight:600;color:var(--weave-fg);
410
+ background:none;border:0;border-radius:var(--weave-radius);
411
+ }
412
+ .weave-raw-toggle:hover{background:var(--weave-line)}
413
+ .weave-raw-toggle:focus-visible{outline:2px solid var(--weave-accent);outline-offset:1px}
414
+ .weave-raw-chevron{flex:none;color:var(--weave-dim);transition:transform .12s ease}
415
+ .weave-raw-toggle[aria-expanded="true"] .weave-raw-chevron{transform:rotate(90deg)}
416
+ .weave-raw-hint{font-size:var(--weave-px-caption);color:var(--weave-faint)}
417
+ .weave-raw-editor{
418
+ display:block;width:100%;box-sizing:border-box;min-height:7em;max-height:18em;
419
+ field-sizing:content;resize:vertical;padding:10px 14px 12px;
420
+ font-family:var(--weave-mono);font-size:var(--weave-px-row);line-height:1.6;
421
+ color:var(--weave-dim);background:transparent;border:0;outline:0;
273
422
  }
423
+ .weave-raw-editor:focus{color:var(--weave-fg)}
274
424
  .weave-note-tags{margin:6px 0 0;display:flex;gap:5px;flex-wrap:wrap}
275
425
  .weave-tag{
276
426
  font-size:var(--weave-px-caption);color:var(--weave-accent);background:var(--weave-panel);
@@ -417,6 +567,113 @@ body{font-size:var(--weave-px-base)}
417
567
  border-top:1px solid var(--weave-line);
418
568
  }
419
569
 
570
+ /* graph spotlight (G1) ---------------------------------------------------
571
+ The field floating top-left over the canvas, and the side sheet top-right.
572
+ Both are overlays on the stage, so they sit on the panel ground with a
573
+ hairline and the pop radius — no elevation shadow, per the sheet's rule.
574
+ The spotlight's voice is --weave-warn: the highlighter colour the WebGL
575
+ side paints lit edges in (graph.model.ts SPOTLIGHT_EDGE_SLOT), so the chip,
576
+ the legend, the marked words and the lit dots all say "lit" in one hue.
577
+ The sheet stops short of the control strip and count line under the
578
+ canvas (about 54px together) and scrolls inside itself. Button rules are
579
+ qualified by their overlay (.weave-sheet .weave-sheet-open) because the
580
+ workspace sheet's \`.weave-workbench button{font:inherit;color:inherit}\`
581
+ reset is installed later and would outrank a single class. */
582
+ .weave-spotlight{
583
+ position:absolute;z-index:3;top:10px;left:10px;width:min(330px,calc(100% - 20px));box-sizing:border-box;
584
+ display:flex;align-items:center;gap:6px;padding:3px 5px 3px 9px;
585
+ color:var(--weave-faint);background:var(--weave-panel);
586
+ border:1px solid var(--weave-line-strong);border-radius:var(--weave-radius-pop);
587
+ }
588
+ /* The pill is the focus ring: the input inside it drops the global outline
589
+ and the frame takes a 2px accent edge (border plus an inset hairline). */
590
+ .weave-spotlight:focus-within{border-color:var(--weave-accent);box-shadow:inset 0 0 0 1px var(--weave-accent)}
591
+ .weave-spotlight-input:focus-visible{outline:none}
592
+ .weave-spotlight-on{color:var(--weave-warn)}
593
+ .weave-spotlight-icon{flex:none;display:block}
594
+ .weave-spotlight-input{
595
+ flex:1;min-width:0;height:24px;padding:0;font:inherit;font-size:var(--weave-px-row);
596
+ color:var(--weave-fg);background:transparent;border:0;outline:0;
597
+ }
598
+ .weave-spotlight-input::placeholder{color:var(--weave-faint)}
599
+ .weave-spotlight-input::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none}
600
+ .weave-spotlight-count{
601
+ flex:none;padding:1px 7px;font-size:var(--weave-px-caption);font-weight:650;white-space:nowrap;
602
+ color:var(--weave-warn);background:color-mix(in srgb,var(--weave-warn) 16%,transparent);border-radius:999px;
603
+ }
604
+ .weave-spotlight-none{color:var(--weave-dim);background:var(--weave-line)}
605
+ .weave-spotlight .weave-spotlight-clear{
606
+ flex:none;padding:1px 5px;cursor:pointer;font-family:var(--weave-mono);font-size:var(--weave-px-prov);
607
+ color:var(--weave-faint);background:transparent;border:1px solid var(--weave-line-strong);border-radius:var(--weave-radius);
608
+ }
609
+ .weave-spotlight .weave-spotlight-clear:hover{color:var(--weave-fg);border-color:var(--weave-accent)}
610
+ .weave-legend-lit{color:var(--weave-warn)}
611
+ .weave-sheet{
612
+ position:absolute;z-index:3;top:10px;right:10px;width:min(300px,calc(100% - 20px));max-height:calc(100% - 74px);
613
+ box-sizing:border-box;overflow:auto;padding:12px 14px 14px;
614
+ font-size:var(--weave-px-base);color:var(--weave-fg);background:var(--weave-panel);
615
+ border:1px solid var(--weave-line-strong);border-radius:var(--weave-radius-pop);
616
+ }
617
+ .weave-sheet .weave-sheet-close{
618
+ position:absolute;top:6px;right:6px;width:24px;height:24px;padding:0;cursor:pointer;
619
+ font-size:var(--weave-px-display);line-height:1;color:var(--weave-dim);
620
+ background:transparent;border:0;border-radius:var(--weave-radius);
621
+ }
622
+ .weave-sheet .weave-sheet-close:hover{color:var(--weave-fg);background:var(--weave-line)}
623
+ .weave-sheet-crumb{
624
+ margin:0 28px 2px 0;display:flex;align-items:center;gap:5px;min-width:0;
625
+ font-size:var(--weave-px-caption);color:var(--weave-faint);
626
+ }
627
+ .weave-sheet-crumb span{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
628
+ .weave-sheet-title{
629
+ margin:0 20px 4px 0;font-size:var(--weave-px-title);font-weight:650;line-height:1.3;overflow-wrap:anywhere;
630
+ }
631
+ .weave-sheet-meta{margin:0;display:flex;align-items:center;gap:5px;font-size:var(--weave-px-ui);color:var(--weave-dim);text-transform:capitalize}
632
+ .weave-sheet-when{color:var(--weave-faint);text-transform:none}
633
+ .weave-sheet-text{
634
+ margin:10px 0 0;padding:8px 10px;line-height:1.55;overflow-wrap:anywhere;
635
+ background:var(--weave-new);border:1px solid color-mix(in srgb,var(--weave-accent) 30%,transparent);
636
+ border-radius:var(--weave-radius-pop);
637
+ }
638
+ .weave-sheet-text-head{
639
+ display:block;margin:0 0 2px;font-size:var(--weave-px-prov);font-weight:700;letter-spacing:.09em;
640
+ text-transform:uppercase;color:var(--weave-accent);
641
+ }
642
+ .weave-sheet-mark{border-radius:var(--weave-radius)}
643
+ .weave-sheet-why{margin:8px 0 0;display:flex;gap:5px;font-size:var(--weave-px-caption);color:var(--weave-dim)}
644
+ .weave-sheet-why span{color:var(--weave-warn)}
645
+ .weave-sheet-section{margin:12px 0 0}
646
+ .weave-sheet-heading{
647
+ margin:0 0 3px;display:flex;align-items:center;font-size:var(--weave-px-caption);font-weight:600;
648
+ letter-spacing:.09em;text-transform:uppercase;color:var(--weave-faint);
649
+ }
650
+ .weave-sheet-count{margin-left:auto;font-family:var(--weave-mono);letter-spacing:0}
651
+ .weave-sheet-rows{margin:0;padding:0;list-style:none}
652
+ .weave-sheet .weave-sheet-row{
653
+ display:flex;align-items:center;gap:7px;width:100%;padding:3px 0;cursor:pointer;text-align:left;
654
+ font:inherit;font-size:var(--weave-px-row);color:var(--weave-fg);background:none;border:0;
655
+ }
656
+ .weave-sheet .weave-sheet-row:hover{color:var(--weave-accent)}
657
+ .weave-sheet-row-label{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
658
+ .weave-sheet-dot{flex:none;width:6px;height:6px;border-radius:999px;background:var(--weave-faint)}
659
+ .weave-sheet-dot-lit{background:var(--weave-warn)}
660
+ .weave-sheet-more{margin:2px 0 0;font-size:var(--weave-px-caption);color:var(--weave-faint)}
661
+ .weave-sheet-actions{margin:14px 0 0;display:flex;gap:6px;flex-wrap:wrap}
662
+ .weave-sheet .weave-sheet-open,.weave-sheet .weave-sheet-split{
663
+ padding:5px 11px;cursor:pointer;font:inherit;font-size:var(--weave-px-ui);font-weight:600;
664
+ border:1px solid var(--weave-line-strong);border-radius:var(--weave-radius);
665
+ }
666
+ .weave-sheet .weave-sheet-open{color:var(--weave-page);background:var(--weave-accent);border-color:var(--weave-accent)}
667
+ .weave-sheet .weave-sheet-open:hover{filter:brightness(1.08)}
668
+ .weave-sheet .weave-sheet-split{color:var(--weave-fg);background:var(--weave-page)}
669
+ .weave-sheet .weave-sheet-split:hover{border-color:var(--weave-accent)}
670
+ /* Side by side the field (330) and the sheet (300) need about 660px of
671
+ stage; a narrower graph — a split pane, a small window — drops the sheet
672
+ under the field instead of letting the two overlap. */
673
+ .weave-graph{container-type:inline-size}
674
+ @container (max-width:680px){.weave-sheet{top:50px;max-height:calc(100% - 114px)}}
675
+ /* end graph spotlight ---------------------------------------------------- */
676
+
420
677
  /* context rail ---------------------------------------------------------- */
421
678
  /* The \`.weave-empty\` block that used to sit above went with \`EmptyState.tsx\`
422
679
  in P3: every column now has its own empty state (\`treeEmptyMessage\`,
@@ -433,6 +690,24 @@ body{font-size:var(--weave-px-base)}
433
690
  border, cursor — plus a full-width flex row so the count badge lands on the
434
691
  right edge of the *column*, not of the text. */
435
692
  .weave-ctx-group{padding:2px var(--weave-gutter) 2px}
693
+ .weave-trail{padding-top:6px;padding-bottom:6px;border-bottom:1px solid var(--weave-line)}
694
+ .weave-trail-head{display:flex;align-items:center;gap:6px;font-size:var(--weave-px-caption);font-weight:600;letter-spacing:.09em;color:var(--weave-faint);text-transform:uppercase}
695
+ .weave-trail-head .weave-ctx-count{margin-left:0}
696
+ .weave-trail-replay,.weave-trail-turn{font:inherit;font-size:var(--weave-px-caption);letter-spacing:0;text-transform:none;color:var(--weave-dim);background:none;border:1px solid var(--weave-line);border-radius:var(--weave-radius);padding:0 6px;cursor:pointer}
697
+ .weave-trail-replay{margin-left:auto}
698
+ .weave-trail-replay:hover,.weave-trail-turn:hover{color:var(--weave-fg)}
699
+ .weave-trail-turn-on{color:var(--weave-accent);border-color:var(--weave-accent)}
700
+ .weave-trail-steps{margin:4px 0 0;padding:0;list-style:none;display:flex;flex-direction:column;gap:2px}
701
+ .weave-trail-step{display:flex;flex-wrap:wrap;align-items:baseline;gap:4px;padding-left:10px;position:relative;line-height:1.45}
702
+ .weave-trail-step::before{content:"";position:absolute;left:0;top:.55em;width:6px;height:6px;border-radius:999px;background:var(--weave-warn)}
703
+ .weave-trail-read::before{background:var(--weave-ok)}
704
+ .weave-trail-wrote::before{background:var(--weave-accent)}
705
+ .weave-trail-verb{color:var(--weave-dim)}
706
+ .weave-trail-detail{color:var(--weave-faint);font-family:var(--weave-mono);font-size:var(--weave-px-caption)}
707
+ .weave-trail-note{font:inherit;color:var(--weave-fg);background:none;border:0;padding:0;cursor:pointer;text-decoration:underline;text-decoration-color:var(--weave-line-strong);text-underline-offset:2px}
708
+ .weave-trail-note:hover{color:var(--weave-accent)}
709
+ .weave-trail-empty{margin:4px 0 0;color:var(--weave-faint)}
710
+ .weave-trail-turns{display:flex;flex-wrap:wrap;gap:4px;margin-top:6px}
436
711
  .weave-ctx-head{margin:0}
437
712
  .weave-ctx-heading{
438
713
  display:flex;align-items:center;gap:5px;width:100%;margin:0;padding:2px 0;
@@ -479,6 +754,7 @@ body{font-size:var(--weave-px-base)}
479
754
  .weave-status-cwd,.weave-status-sel{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
480
755
  .weave-status-cwd{max-width:38%;color:var(--weave-faint)}
481
756
  .weave-status-sel{color:var(--weave-fg)}
757
+ .weave-status-hint{margin-left:auto;padding-left:8px;white-space:nowrap;color:var(--weave-faint)}
482
758
 
483
759
  /* overlays: the ⌘K palette and the ? help sheet (P4) --------------------- */
484
760
  /* The scrim is a click target that closes, and the reason both overlays sit
@@ -494,31 +770,77 @@ body{font-size:var(--weave-px-base)}
494
770
  overflow:hidden;background:var(--weave-panel);color:var(--weave-fg);
495
771
  border:1px solid var(--weave-line-strong);border-radius:var(--weave-radius-pop);
496
772
  }
773
+ /* The ⌘K palette, grouped and compact: an input row (glyph, query, Esc), a
774
+ scope row of pills that Tab cycles, one line per result under small
775
+ capitals group heads, and a footer of key hints. Wider than the help sheet
776
+ because a row carries a title, a snippet and a meta column side by side. */
777
+ .weave-palette{max-width:640px}
778
+ .weave-palette-bar{
779
+ flex:none;display:flex;align-items:center;gap:8px;padding:0 11px;
780
+ border-bottom:1px solid var(--weave-line);
781
+ }
782
+ .weave-palette-glyph{color:var(--weave-faint)}
497
783
  .weave-palette-input{
498
- flex:none;height:34px;padding:0 11px;font:inherit;font-size:var(--weave-px-subhead);
499
- color:var(--weave-fg);background:transparent;border:0;
500
- border-bottom:1px solid var(--weave-line);border-radius:0;
784
+ flex:1;min-width:0;height:36px;padding:0;font:inherit;font-size:var(--weave-px-subhead);
785
+ color:var(--weave-fg);background:transparent;border:0;border-radius:0;
786
+ }
787
+ .weave-palette-input:focus-visible{outline:0}
788
+ .weave-palette-input::-webkit-search-cancel-button{display:none}
789
+ .weave-palette-bar:focus-within{box-shadow:inset 0 -1px 0 var(--weave-accent)}
790
+ .weave-palette-kbd{
791
+ flex:none;padding:0 4px;font-family:inherit;font-size:var(--weave-px-prov);line-height:14px;
792
+ color:var(--weave-faint);border:1px solid var(--weave-line-strong);border-radius:var(--weave-radius);
793
+ }
794
+ .weave-palette-scopes{
795
+ flex:none;display:flex;flex-wrap:wrap;gap:3px;padding:5px 8px;
796
+ border-bottom:1px solid var(--weave-line);
501
797
  }
502
- .weave-palette-input:focus-visible{outline-offset:-2px}
503
- .weave-hits{flex:1;min-height:0;overflow:auto;margin:0;padding:3px 0;list-style:none}
798
+ .weave-palette-scope{
799
+ font:inherit;font-size:var(--weave-px-caption);line-height:1;padding:3px 9px;cursor:pointer;
800
+ color:var(--weave-dim);background:none;border:0;border-radius:999px;
801
+ }
802
+ .weave-palette-scope:hover{color:var(--weave-fg);background:var(--weave-line)}
803
+ .weave-palette-scope[aria-pressed="true"]{color:var(--weave-panel);background:var(--weave-fg);font-weight:600}
804
+ .weave-palette-scope-count{font-variant-numeric:tabular-nums}
805
+ .weave-hits{flex:1;min-height:0;overflow:auto;margin:0;padding:0 0 4px;list-style:none}
806
+ .weave-hit-section{list-style:none}
807
+ .weave-hit-group{
808
+ padding:7px 11px 2px;font-size:var(--weave-px-prov);font-weight:700;letter-spacing:.08em;
809
+ text-transform:uppercase;color:var(--weave-faint);
810
+ }
811
+ .weave-hit-rows{margin:0;padding:0;list-style:none}
504
812
  .weave-hit{
505
- display:flex;align-items:baseline;gap:7px;padding:3px 11px;cursor:pointer;
506
- min-width:0;white-space:nowrap;
813
+ display:flex;align-items:center;gap:8px;padding:3px 11px;cursor:pointer;
814
+ min-width:0;white-space:nowrap;font-size:var(--weave-px-row);
507
815
  }
508
816
  .weave-hit-on{background:var(--weave-new);box-shadow:inset 2px 0 0 var(--weave-accent)}
509
- .weave-hit-badge{
510
- flex:none;font-size:var(--weave-px-prov);letter-spacing:.06em;text-transform:uppercase;color:var(--weave-faint);
511
- min-width:56px;
512
- }
513
- .weave-hit-note .weave-hit-badge{color:var(--weave-accent)}
514
- .weave-hit-label{flex:none;max-width:46%;overflow:hidden;text-overflow:ellipsis}
817
+ .weave-hit-icon{color:var(--weave-faint)}
818
+ .weave-hit-notes .weave-hit-icon{color:var(--weave-accent)}
819
+ .weave-hit-label{flex:none;max-width:46%;overflow:hidden;text-overflow:ellipsis;font-weight:600;color:var(--weave-fg)}
820
+ .weave-hit-mono{font-family:var(--weave-mono);font-size:var(--weave-px-ui);font-weight:500}
515
821
  .weave-hit-detail{flex:1;min-width:0;overflow:hidden;text-overflow:ellipsis;font-size:var(--weave-px-ui);color:var(--weave-dim)}
822
+ /* The match marks (search rows, the graph sheet) are a find-in-page amber.
823
+ Dark themes tint their own light --weave-warn, and the marked words go
824
+ white: the theme's text on that tint measured 3.3–3.6:1 in the Catppuccin
825
+ darks, white is 5.5:1 or better in every dark theme. A light theme's
826
+ --weave-warn is a deep brown that tints to mud, so light schemes take a
827
+ fixed amber under their own text instead. */
828
+ .weave-hit-mark,.weave-sheet-mark{color:#fff;background:color-mix(in srgb,var(--weave-warn) 28%,transparent)}
829
+ .weave-hit-mark{border-radius:var(--weave-radius);padding:0 1px}
830
+ :root[data-weave-scheme="light"] .weave-hit-mark,:root[data-weave-scheme="light"] .weave-sheet-mark{color:var(--weave-fg);background:color-mix(in srgb,#f2a93b 34%,transparent)}
831
+ .weave-hit-meta{flex:none;margin-left:auto;font-size:var(--weave-px-caption);color:var(--weave-faint);font-variant-numeric:tabular-nums}
516
832
  .weave-palette-status{margin:0;padding:14px 11px;color:var(--weave-dim);line-height:1.5}
517
833
  .weave-palette-foot,.weave-help-foot{
518
834
  display:flex;gap:12px;margin:0;padding:4px 11px;font-size:var(--weave-px-caption);color:var(--weave-faint);
519
835
  border-top:1px solid var(--weave-line);
520
836
  }
521
- .weave-palette-hint,.weave-help-hint{margin-left:auto}
837
+ .weave-palette-foot{align-items:center;flex-wrap:wrap;row-gap:2px;min-height:26px;box-sizing:border-box;background:var(--weave-bg)}
838
+ .weave-palette-light{
839
+ margin-left:auto;display:flex;align-items:center;gap:6px;padding:0;cursor:pointer;white-space:nowrap;
840
+ font:inherit;font-weight:600;color:var(--weave-accent);background:none;border:0;
841
+ }
842
+ .weave-palette-light .weave-palette-kbd{color:inherit;border-color:currentColor}
843
+ .weave-help-hint{margin-left:auto}
522
844
  .weave-help-title{margin:0;padding:9px 11px;font-size:var(--weave-px-base);font-weight:600;border-bottom:1px solid var(--weave-line)}
523
845
  .weave-keys{flex:1;min-height:0;overflow:auto;margin:0;padding:5px 11px}
524
846
  .weave-key-group{margin:0 0 3px;font-size:var(--weave-px-prov);letter-spacing:.09em;text-transform:uppercase;color:var(--weave-faint)}
@@ -5,7 +5,7 @@ export const WORKSPACE_CSS = `
5
5
  .weave-workbench button:focus-visible,.weave-workbench summary:focus-visible{outline:2px solid var(--weave-accent);outline-offset:-2px}
6
6
  .weave-workbench button:disabled{opacity:.35;cursor:default}
7
7
  .weave-ribbon{width:44px;flex:none;display:flex;align-items:center;flex-direction:column;gap:8px;padding:12px 0;background:var(--weave-panel);border-right:1px solid var(--weave-line)}
8
- .weave-ribbon button{width:32px;height:32px;border:0;border-radius:var(--weave-radius);background:transparent;font-size:var(--weave-px-display);color:var(--weave-dim)}
8
+ .weave-ribbon button{width:32px;height:32px;display:grid;place-items:center;border:0;border-radius:var(--weave-radius);background:transparent;font-size:var(--weave-px-display);color:var(--weave-dim)}
9
9
  .weave-ribbon button:hover,.weave-ribbon button[aria-pressed=true]{color:var(--weave-accent);background:var(--weave-new)}
10
10
  .weave-ribbon-space{flex:1}
11
11
  .weave-sidebar{flex:none;min-width:0;display:flex;flex-direction:column;background:var(--weave-panel);overflow:hidden}
@@ -65,18 +65,24 @@ export const WORKSPACE_CSS = `
65
65
  .weave-pane-menu button{text-align:left;border:0;background:none;padding:9px 10px;font-size:var(--weave-px-row)}
66
66
  .weave-pane-menu button:hover{background:var(--weave-new)}
67
67
  .weave-pane-content{min-width:0;min-height:0;display:flex;overflow:hidden;position:relative}
68
- .weave-document{display:flex;min-width:0;min-height:0;flex:1}
68
+ /* The reading column's gutters. Centred at the reading width; where the
69
+ floating Context card covers the right margin, the right gutter grows to
70
+ clear it and the column slides left before it narrows. */
71
+ .weave-document{display:flex;min-width:0;min-height:0;flex:1;
72
+ --weave-gutter-c:max(24px,calc((100% - var(--weave-reading-width,760px))/2));
73
+ --weave-gutter-r:max(var(--weave-gutter-c),var(--weave-card-space,0px));
74
+ --weave-gutter-l:max(24px,min(var(--weave-gutter-c),calc(100% - var(--weave-reading-width,760px) - var(--weave-gutter-r))))}
69
75
  .weave-document .weave-note{width:100%;border-left:0}
70
- .weave-document .weave-note-head{position:static;padding:28px max(24px,calc((100% - var(--weave-reading-width,760px))/2)) 18px;border:0}
71
- .weave-document .weave-note-title{font-size:var(--weave-px-display);letter-spacing:-.03em;margin-bottom:12px}
72
- .weave-document .weave-note-body{padding:10px max(24px,calc((100% - var(--weave-reading-width,760px))/2)) 64px;font-size:var(--weave-note-size,var(--weave-px-subhead));line-height:1.8}
76
+ .weave-document .weave-note-head{position:static;padding:28px var(--weave-gutter-r) 18px var(--weave-gutter-l);border:0}
77
+ .weave-document .weave-note-title{font-size:var(--weave-px-display);letter-spacing:-.03em;margin-bottom:10px}
78
+ .weave-document .weave-note-body{padding:10px var(--weave-gutter-r) 64px var(--weave-gutter-l);font-size:var(--weave-note-size,var(--weave-px-subhead));line-height:1.8}
73
79
  .weave-document .weave-note-body h1{font-size:var(--weave-px-display);line-height:1.4;margin-bottom:18px}
74
80
  .weave-document .weave-note-body h2{font-size:var(--weave-px-title);margin-top:28px}
75
- .weave-document .weave-note-editor{padding:14px max(24px,calc((100% - var(--weave-reading-width,760px))/2));min-height:220px;flex:1;font-size:var(--weave-note-size,var(--weave-px-subhead))}
81
+ .weave-document .weave-note-editor{padding:14px var(--weave-gutter-r) 14px var(--weave-gutter-l);min-height:220px;flex:1;font-size:var(--weave-note-size,var(--weave-px-subhead))}
76
82
  .weave-document .weave-note-tags{margin-top:12px}
77
- .weave-document .weave-note-edit,.weave-document .weave-note-save{padding:5px 10px;border-radius:var(--weave-radius);font-size:var(--weave-px-caption)}
78
- .weave-document .weave-note-edit{color:var(--weave-dim);background:transparent;border-color:transparent}
79
- .weave-document .weave-note-edit:hover{color:var(--weave-accent);background:var(--weave-new);border-color:transparent}
83
+ .weave-document .weave-replay{padding:12px var(--weave-gutter-r) 14px var(--weave-gutter-l)}
84
+ .weave-document .weave-raw{margin:0 var(--weave-gutter-r) 28px var(--weave-gutter-l)}
85
+ .weave-document .weave-editbar{padding-right:calc(12px + var(--weave-card-space,0px))}
80
86
  .weave-welcome{align-self:center;margin:auto;padding:35px;max-width:560px;color:var(--weave-dim);line-height:1.7}
81
87
  .weave-welcome-mark{font-size:var(--weave-px-display);color:var(--weave-accent)}
82
88
  .weave-welcome h1{font-size:var(--weave-px-display);line-height:1.4;letter-spacing:-.025em;color:var(--weave-fg);font-weight:550}
@@ -115,6 +121,29 @@ export const WORKSPACE_CSS = `
115
121
  .weave-footer>.weave-status{flex:1;min-width:0;border:0}
116
122
  .weave-footer>span{padding:4px 10px;white-space:nowrap}
117
123
  .weave-footer>button{font:inherit;color:var(--weave-fg);background:none;border:0;padding:7px 12px;cursor:pointer}
124
+ /* Context as a floating card (C1): over the page's right margin, below the
125
+ pane toolbar, folding to a pill in the toolbar strip. Pinned, it is the
126
+ column it always was. The shadow is the card's only elevation, and lives
127
+ here rather than in THEME_CSS, whose surfaces are flat by rule. */
128
+ .weave-context-dock.weave-context-float{position:absolute;top:90px;right:14px;bottom:auto;z-index:10;width:var(--weave-card-width);max-height:calc(100% - 104px);border:1px solid var(--weave-line-strong);border-radius:var(--weave-radius-pop);background:var(--weave-panel);box-shadow:0 12px 32px #0000001f,0 2px 6px #00000014;transition:opacity 160ms ease,visibility 0s}
129
+ .weave-context-dock.weave-context-float[data-open=false]{width:var(--weave-card-width);opacity:0;visibility:hidden;transition:opacity 160ms ease,visibility 0s 160ms}
130
+ .weave-context-float>.weave-workspace-divider{display:none}
131
+ .weave-context-float>.weave-sidebar,.weave-context-float[data-open=false]>.weave-sidebar{width:100%;max-height:100%;border:0;background:none;transform:none}
132
+ .weave-context-float .weave-sidebar-heading{height:38px;padding:0 6px 0 12px;border-bottom:1px solid var(--weave-line)}
133
+ .weave-context-float .weave-rail{padding:4px 12px 10px}
134
+ .weave-context-float .weave-context-graph-slot{max-height:calc(100dvh - 260px)}
135
+ .weave-context-float .weave-context-tags{height:auto!important;max-height:220px}
136
+ .weave-context-float .weave-ctx-group>.weave-workspace-divider{display:none}
137
+ .weave-context-heading strong{color:var(--weave-fg)}
138
+ .weave-context-count{min-width:18px;padding:1px 6px;border-radius:999px;background:var(--weave-raise);color:var(--weave-dim);font-size:var(--weave-px-caption);line-height:1.4;text-align:center}
139
+ .weave-sidebar-heading .weave-context-pin{margin-left:auto}
140
+ .weave-context-heading button:last-child{margin-left:0}
141
+ .weave-context-heading .weave-context-pin[aria-pressed=true]{color:var(--weave-accent)}
142
+ .weave-context-heading button{display:grid;place-items:center;width:28px;height:28px;padding:0}
143
+ .weave-context-pill{position:absolute;top:47px;right:54px;z-index:10;display:inline-flex;align-items:center;gap:6px;height:26px;padding:0 6px 0 9px;border:1px solid var(--weave-line-strong);border-radius:999px;background:var(--weave-panel);color:var(--weave-dim)!important;font-size:var(--weave-px-caption)!important;box-shadow:0 2px 8px #00000014}
144
+ .weave-context-pill:hover{color:var(--weave-fg)!important;border-color:var(--weave-accent)}
145
+ .weave-context-pill .weave-icon{width:13px;height:13px}
146
+ .weave-pane-under-card{--weave-card-space:calc(var(--weave-card-width) + 28px)}
118
147
  @media(max-width:1049px){
119
148
  .weave-context-dock{position:absolute;right:0;top:0;bottom:0;z-index:10;width:var(--weave-sidebar-width)}
120
149
  .weave-sidebar-context{border-left:1px solid var(--weave-line-strong)}
@@ -128,6 +157,8 @@ export const WORKSPACE_CSS = `
128
157
  .weave-panes{grid-template-columns:minmax(0,1fr)!important;grid-template-rows:minmax(0,1fr)!important}
129
158
  .weave-document .weave-note-head{padding:20px}
130
159
  .weave-document .weave-note-body{padding:10px 20px 40px}
160
+ .weave-document .weave-replay{padding:12px 20px}
161
+ .weave-document .weave-raw{margin:0 20px 24px}
131
162
  }
132
163
  @media(prefers-reduced-motion:reduce){
133
164
  .weave-sidebar-dock,.weave-sidebar-dock[data-open=false],.weave-sidebar-dock>.weave-sidebar{transition:none}
@@ -0,0 +1,55 @@
1
+ /**
2
+ * The "This turn" strip at the top of the context rail (issue #78).
3
+ *
4
+ * Every string and every grouping comes from `trail.model.ts`; this renders
5
+ * the newest turn's steps as `searched "auth" → read Auth migration → …`,
6
+ * with each note a link, and the earlier turns as small time chips that
7
+ * replay their glow when clicked.
8
+ */
9
+
10
+ import type { TrailTurnView } from "./trail.model";
11
+ import { TRAIL_EMPTY_TURN, turnHeading } from "./trail.model";
12
+
13
+ export interface TrailStripProps {
14
+ turns: readonly TrailTurnView[];
15
+ /** The turn whose steps are listed — the replayed one, or `null` for the newest. */
16
+ shownTurn: number | null;
17
+ onSelect: (id: string, newTab?: boolean) => void;
18
+ onReplay: (turn: number) => void;
19
+ }
20
+
21
+ export function TrailStrip(props: TrailStripProps) {
22
+ const latest = props.turns[0];
23
+ if (latest === undefined) return null;
24
+ const shown = props.turns.find((view) => view.turn === props.shownTurn) ?? latest;
25
+ return (
26
+ <section class="weave-ctx-group weave-trail" aria-label="What your agent recalled">
27
+ <h4 class="weave-ctx-head weave-trail-head">
28
+ <span class="weave-trail-title">{turnHeading(shown, shown === latest)}</span>
29
+ <span class="weave-ctx-count" title="Notes read or written">{shown.used}</span>
30
+ {shown.steps.length === 0 ? null : <button type="button" class="weave-trail-replay" title="Replay this turn's glow" onClick={() => props.onReplay(shown.turn)}>Replay</button>}
31
+ </h4>
32
+ {shown.steps.length === 0 ? <p class="weave-trail-empty">{TRAIL_EMPTY_TURN}</p> : null}
33
+ <ol class="weave-trail-steps">
34
+ {shown.steps.map((step) => (
35
+ <li key={step.key} class={`weave-trail-step weave-trail-${step.kind}`}>
36
+ <span class="weave-trail-verb">{step.verb}</span>
37
+ {step.notes.map((note) => (
38
+ <button key={note.id} type="button" class="weave-trail-note" onClick={(event) => props.onSelect(note.id, event.metaKey || event.ctrlKey)}>{note.label}</button>
39
+ ))}
40
+ {step.detail === null ? null : <span class="weave-trail-detail">{step.detail}</span>}
41
+ </li>
42
+ ))}
43
+ </ol>
44
+ {props.turns.length < 2 ? null : (
45
+ <div class="weave-trail-turns" role="group" aria-label="Earlier turns">
46
+ {props.turns.map((view, index) => (
47
+ <button key={view.turn} type="button" class={`weave-trail-turn${view.turn === shown.turn ? " weave-trail-turn-on" : ""}`} aria-pressed={view.turn === shown.turn} onClick={() => props.onReplay(view.turn)}>
48
+ {turnHeading(view, index === 0)}
49
+ </button>
50
+ ))}
51
+ </div>
52
+ )}
53
+ </section>
54
+ );
55
+ }