pi-weave 0.1.7 → 0.1.9

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 (98) hide show
  1. package/README.md +123 -37
  2. package/package.json +17 -5
  3. package/skills/weave-notepad/SKILL.md +13 -4
  4. package/src/core/cache/workspace.ts +466 -0
  5. package/src/core/concurrency.ts +36 -0
  6. package/src/core/frontmatter.ts +270 -23
  7. package/src/core/git.ts +19 -0
  8. package/src/core/graph/build.ts +97 -5
  9. package/src/core/graph/current.ts +41 -28
  10. package/src/core/graph/mentions.ts +170 -0
  11. package/src/core/graph/model.ts +39 -0
  12. package/src/core/graph/wikilinks.ts +5 -1
  13. package/src/core/index.ts +14 -0
  14. package/src/core/openInEditor.ts +69 -0
  15. package/src/core/paths.ts +7 -0
  16. package/src/core/sessions.ts +929 -0
  17. package/src/core/summaries.ts +1 -19
  18. package/src/core/types.ts +40 -0
  19. package/src/core/vault.ts +739 -57
  20. package/src/core/view/cluster.ts +262 -0
  21. package/src/core/view/detail.ts +118 -0
  22. package/src/core/view/focus.ts +109 -0
  23. package/src/core/view/health.ts +156 -0
  24. package/src/core/view/index.ts +15 -0
  25. package/src/core/view/links.ts +105 -0
  26. package/src/core/view/time.ts +47 -0
  27. package/src/core/view/tree.ts +269 -0
  28. package/src/core/view/types.ts +39 -0
  29. package/src/core/workspace.ts +3 -3
  30. package/src/pi/index.ts +248 -48
  31. package/src/pi/sessionScan.ts +104 -0
  32. package/src/pi/summarize.ts +24 -4
  33. package/src/pi/tools/noteTool.ts +13 -4
  34. package/src/pi/viewer/tui/branding.ts +8 -7
  35. package/src/pi/viewer/tui/explorer.ts +5 -3
  36. package/src/pi/viewer/tui/model.ts +46 -667
  37. package/src/pi/viewer/tui/openNote.ts +7 -56
  38. package/src/pi/viewer/tui/surface/explore.ts +4 -2
  39. package/src/pi/viewer/web/run.ts +331 -0
  40. package/src/web/client/api.dom.ts +40 -0
  41. package/src/web/client/api.ts +472 -0
  42. package/src/web/client/bootstrap.ts +58 -0
  43. package/src/web/client/context/context.model.ts +313 -0
  44. package/src/web/client/dist/app.js +764 -0
  45. package/src/web/client/graph/Graph.tsx +209 -0
  46. package/src/web/client/graph/column.model.ts +372 -0
  47. package/src/web/client/graph/dynamics.ts +176 -0
  48. package/src/web/client/graph/graph.model.ts +546 -0
  49. package/src/web/client/graph/positions.ts +380 -0
  50. package/src/web/client/graph/project.ts +153 -0
  51. package/src/web/client/graph/renderer.dom.ts +52 -0
  52. package/src/web/client/graph/renderer.ts +339 -0
  53. package/src/web/client/graph/scheme.ts +44 -0
  54. package/src/web/client/live.model.ts +275 -0
  55. package/src/web/client/live.ts +151 -0
  56. package/src/web/client/main.tsx +27 -0
  57. package/src/web/client/note/Editor.tsx +102 -0
  58. package/src/web/client/note/Note.tsx +113 -0
  59. package/src/web/client/note/editor.controller.ts +151 -0
  60. package/src/web/client/note/editor.model.ts +636 -0
  61. package/src/web/client/note/note.model.ts +738 -0
  62. package/src/web/client/search/SearchPalette.tsx +105 -0
  63. package/src/web/client/search/search.model.ts +588 -0
  64. package/src/web/client/search/search.ts +107 -0
  65. package/src/web/client/selection.storage.ts +69 -0
  66. package/src/web/client/shell/Columns.tsx +161 -0
  67. package/src/web/client/shell/ContextRail.tsx +87 -0
  68. package/src/web/client/shell/Divider.tsx +44 -0
  69. package/src/web/client/shell/FocusTrap.tsx +56 -0
  70. package/src/web/client/shell/Header.tsx +64 -0
  71. package/src/web/client/shell/HelpOverlay.tsx +70 -0
  72. package/src/web/client/shell/Shell.tsx +210 -0
  73. package/src/web/client/shell/StatusBar.tsx +28 -0
  74. package/src/web/client/shell/cssvars.ts +70 -0
  75. package/src/web/client/shell/drag.model.ts +170 -0
  76. package/src/web/client/shell/focus.model.ts +100 -0
  77. package/src/web/client/shell/keys.model.ts +453 -0
  78. package/src/web/client/shell/keys.ts +59 -0
  79. package/src/web/client/shell/layout.model.ts +526 -0
  80. package/src/web/client/shell/shell.model.ts +333 -0
  81. package/src/web/client/shell/theme.ts +490 -0
  82. package/src/web/client/shell/viewport.ts +29 -0
  83. package/src/web/client/state.ts +89 -0
  84. package/src/web/client/tree/Tree.tsx +141 -0
  85. package/src/web/client/tree/tree.model.ts +674 -0
  86. package/src/web/client/workspace.ts +278 -0
  87. package/src/web/server/page.ts +258 -0
  88. package/src/web/server/routes.ts +987 -0
  89. package/src/web/server/security.ts +361 -0
  90. package/src/web/server/server.ts +275 -0
  91. package/src/web/server/sse.ts +321 -0
  92. package/src/web/server/watcher.ts +507 -0
  93. package/src/web/shared/graph.ts +213 -0
  94. package/src/web/shared/layout.ts +314 -0
  95. package/src/web/shared/logo.ts +11 -0
  96. package/src/web/shared/metrics.ts +136 -0
  97. package/src/web/shared/view.ts +200 -0
  98. package/src/web/shared/wire.ts +358 -0
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Live force dynamics for the graph's current shape — the Obsidian-style
3
+ * "the graph is alive" feel while the user drags.
4
+ *
5
+ * ## One physics, two drivers
6
+ *
7
+ * The static layout (`shared/layout.ts`) and this module now share ONE force
8
+ * configuration: {@link createForceSimulation}. They used to be two
9
+ * independent physics — the static path ring-sized its link distances per
10
+ * fan-out and anchored every node to its seed; this one pulled at a flat 90
11
+ * units toward its own equilibrium. Every graph therefore *visibly re-laid
12
+ * itself* after mount (a 215-node repo graph migrated from a ~1200-unit ring
13
+ * to the bespoke sim's ~300-unit cloud over about thirty seconds), and the
14
+ * constant-alpha loop ticked at 60 fps forever, because a bespoke integrator
15
+ * has no alpha to decay. Sharing the factory fixes both by construction: the
16
+ * warm start is already at the shared equilibrium, so there is nothing to
17
+ * migrate to, and d3's alpha decay is what says "settled".
18
+ *
19
+ * ## The lifecycle
20
+ *
21
+ * A new engine starts at {@link SETTLE_ALPHA} (an expand/collapse lands new
22
+ * nodes already laid out; the brief settle is the "alive" response, not a
23
+ * re-layout) and decays to d3's alpha floor, at which point {@link awake}
24
+ * turns false and the component's clock puts itself to sleep — no frames are
25
+ * spent on a graph that is holding still. `pin` re-heats to
26
+ * {@link DRAG_ALPHA_TARGET} (d3's own drag pattern), so neighbours make room
27
+ * while the user drags; `release` clears the pin AND retargets the node's
28
+ * anchor to its drop point, so a dragged node stays where it was dropped
29
+ * instead of springing back to its seed.
30
+ *
31
+ * ## The §7.2 vertical-line freeze
32
+ *
33
+ * Inherited from d3 itself: `manyBody`, `collide` and `link` all inject a
34
+ * seeded `jiggle` into a zero axis component, so coincident or collinear
35
+ * nodes always acquire a direction. The seeded LCG (`seed: 1`) keeps the
36
+ * whole thing deterministic — the same graph, the same gestures, byte-identical
37
+ * positions — which is what the test gate asserts.
38
+ */
39
+
40
+ import type { Point } from "../../shared/layout";
41
+ import { createForceSimulation, isContainment } from "../../shared/layout";
42
+ import type { WireEdgeKind } from "../../shared/graph";
43
+ import type { RenderGraph } from "./graph.model";
44
+
45
+ interface SimNode {
46
+ id: string;
47
+ x?: number;
48
+ y?: number;
49
+ vx?: number;
50
+ vy?: number;
51
+ index?: number;
52
+ fx?: number | null;
53
+ fy?: number | null;
54
+ }
55
+
56
+ interface SimLink {
57
+ source: string | SimNode;
58
+ target: string | SimNode;
59
+ kind: WireEdgeKind;
60
+ }
61
+
62
+ export interface GraphSimulation {
63
+ tick(): void;
64
+ positions(): Map<string, Point>;
65
+ pin(id: string, at: Point): void;
66
+ release(id: string): void;
67
+ /** `false` once the alpha floor is reached — the graph is holding still, and the clock can sleep. */
68
+ awake(): boolean;
69
+ }
70
+
71
+ /** Re-heat on creation (mount, expand, collapse): brief and local, never a re-layout. */
72
+ const SETTLE_ALPHA = 0.06;
73
+ /**
74
+ * The drag alpha target. The balance the user feels: too low and the
75
+ * neighbours read as stiff and lazy while the node moves; too high and the
76
+ * whole tree thrashes (measured: the hub drag peaks at ~950px/s of per-node
77
+ * ripple at 0.3 with rigid links, ~174px/s at 0.2 with the soft springs —
78
+ * lively, and an order of magnitude inside the crazy regime).
79
+ */
80
+ const DRAG_ALPHA_TARGET = 0.2;
81
+ /** d3-force's alpha floor. */
82
+ const ALPHA_MIN = 0.001;
83
+
84
+ /**
85
+ * Build a live simulation over a {@link RenderGraph}.
86
+ *
87
+ * `initial`, when given, is the warm start: existing ids keep their current
88
+ * positions (so a drag or an expand does not make the graph jump), and ids it
89
+ * does not name fall back to the graph's own laid-out positions. Those warm
90
+ * positions are also the anchor targets, so the graph rests exactly where the
91
+ * layout left it.
92
+ *
93
+ * Returns `null` for an empty graph — there is nothing to simulate, and an
94
+ * empty `Map` every frame is work for nothing.
95
+ */
96
+ export function createGraphSimulation(graph: RenderGraph, initial?: ReadonlyMap<string, Point>): GraphSimulation | null {
97
+ if (graph.nodes.length === 0) return null;
98
+
99
+ const nodes: SimNode[] = graph.nodes.map((node) => {
100
+ const at = initial?.get(node.id);
101
+ return {
102
+ id: node.id,
103
+ x: at !== undefined && Number.isFinite(at.x) ? at.x : node.x,
104
+ y: at !== undefined && Number.isFinite(at.y) ? at.y : node.y,
105
+ vx: 0,
106
+ vy: 0,
107
+ };
108
+ });
109
+ const byId = new Map(nodes.map((node) => [node.id, node]));
110
+
111
+ // Dangling endpoints and self-edges are a degenerate *input*; forceLink
112
+ // would throw on them, so they are dropped here and the layout's own
113
+ // `pathologicalGraph` fixture remains the gate for degenerate *outputs*.
114
+ const seen = new Set<string>();
115
+ const links: SimLink[] = [];
116
+ for (const edge of graph.edges) {
117
+ if (edge.source === edge.target) continue;
118
+ if (!byId.has(edge.source) || !byId.has(edge.target)) continue;
119
+ const key = `${edge.source}\u0000${edge.target}\u0000${edge.kind}`;
120
+ if (seen.has(key)) continue;
121
+ seen.add(key);
122
+ links.push({ source: edge.source, target: edge.target, kind: edge.kind });
123
+ }
124
+
125
+ // No invented seeding; centre gravity lives in the factory (`forceX()`/
126
+ // `forceY()`). `anchors` starts empty — every node targets the origin — and
127
+ // grows on release: a dropped node's anchor is retargeted to its drop point,
128
+ // so placement sticks (the anchor is five times stronger than the leaf
129
+ // spring, so the node rests where the user left it).
130
+ const anchors = new Map<string, Point>();
131
+ const sim = createForceSimulation({ nodes, links, anchors, seed: 1 })
132
+ .alpha(SETTLE_ALPHA)
133
+ .alphaMin(ALPHA_MIN);
134
+
135
+ return {
136
+ tick() {
137
+ sim.tick();
138
+ },
139
+
140
+ awake() {
141
+ // A held alpha target is motion about to happen — the clock must stay
142
+ // armed even while alpha itself is still converging up to the target.
143
+ return sim.alphaTarget() > ALPHA_MIN || sim.alpha() > ALPHA_MIN;
144
+ },
145
+
146
+ positions() {
147
+ return new Map(nodes.map((node) => [node.id, { x: node.x ?? 0, y: node.y ?? 0 }]));
148
+ },
149
+
150
+ pin(id, at) {
151
+ const node = byId.get(id);
152
+ if (!node) return;
153
+ // d3's canonical drag, from the force-directed graph example: fix the
154
+ // subject at the pointer and hold the alpha target up so the neighbours
155
+ // make room, then cool on release. A pinned node is immune to every
156
+ // force — the drag must not fight the sim, or the node would shudder
157
+ // under its own neighbours.
158
+ node.fx = at.x;
159
+ node.fy = at.y;
160
+ sim.alphaTarget(DRAG_ALPHA_TARGET);
161
+ },
162
+
163
+ release(id) {
164
+ const node = byId.get(id);
165
+ if (!node) return;
166
+ // Unfix, like d3's `dragended` — and retarget the node's centre-gravity
167
+ // anchor to the drop point, so the graph reads the drop as a placement:
168
+ // the node rests where the user left it instead of gliding back into
169
+ // the tree.
170
+ anchors.set(id, { x: node.x ?? 0, y: node.y ?? 0 });
171
+ node.fx = null;
172
+ node.fy = null;
173
+ sim.alphaTarget(0);
174
+ },
175
+ };
176
+ }