@xzzpig/pi-starline 0.1.0

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 (45) hide show
  1. package/LICENSE +22 -0
  2. package/README.md +223 -0
  3. package/extensions/starline/config.ts +1317 -0
  4. package/extensions/starline/editor-cursor.ts +55 -0
  5. package/extensions/starline/editor-factory-marker.ts +49 -0
  6. package/extensions/starline/editor-metadata-format.ts +259 -0
  7. package/extensions/starline/extension-status.ts +88 -0
  8. package/extensions/starline/footer-format.ts +279 -0
  9. package/extensions/starline/footer.ts +744 -0
  10. package/extensions/starline/format.ts +473 -0
  11. package/extensions/starline/git.ts +467 -0
  12. package/extensions/starline/icons.ts +294 -0
  13. package/extensions/starline/index.ts +651 -0
  14. package/extensions/starline/live-context.ts +75 -0
  15. package/extensions/starline/mouse/capabilities.ts +207 -0
  16. package/extensions/starline/mouse/component-tree.ts +230 -0
  17. package/extensions/starline/mouse/editor-caret.ts +498 -0
  18. package/extensions/starline/mouse/editor-hit-test.ts +133 -0
  19. package/extensions/starline/mouse/editor-mouse.ts +142 -0
  20. package/extensions/starline/mouse/editor-scroll.ts +119 -0
  21. package/extensions/starline/mouse/editor-text-cursor.ts +209 -0
  22. package/extensions/starline/mouse/editor-text-edit.ts +90 -0
  23. package/extensions/starline/mouse/hint.ts +14 -0
  24. package/extensions/starline/mouse/hit-test.ts +144 -0
  25. package/extensions/starline/mouse/index.ts +1119 -0
  26. package/extensions/starline/mouse/selection-state.ts +68 -0
  27. package/extensions/starline/mouse/tool-box.ts +290 -0
  28. package/extensions/starline/mouse/transcript-copy.ts +166 -0
  29. package/extensions/starline/mouse/word-select.ts +145 -0
  30. package/extensions/starline/package-version.ts +650 -0
  31. package/extensions/starline/paste-collapse.ts +312 -0
  32. package/extensions/starline/pill-config.ts +122 -0
  33. package/extensions/starline/pill.ts +295 -0
  34. package/extensions/starline/project-refresh.ts +104 -0
  35. package/extensions/starline/project-state.ts +66 -0
  36. package/extensions/starline/prototype-patch-registry.ts +136 -0
  37. package/extensions/starline/runtime.ts +841 -0
  38. package/extensions/starline/selector-border.ts +77 -0
  39. package/extensions/starline/session-lifecycle.ts +60 -0
  40. package/extensions/starline/settings-command.ts +925 -0
  41. package/extensions/starline/state.ts +65 -0
  42. package/extensions/starline/style.ts +528 -0
  43. package/extensions/starline/ui.ts +611 -0
  44. package/extensions/starline/user-message.ts +267 -0
  45. package/package.json +60 -0
@@ -0,0 +1,144 @@
1
+ /**
2
+ * Which of Pi's components owns a screen cell.
3
+ *
4
+ * Pi's layout tree carries every component's rectangle, so a tool box is
5
+ * identified by identity rather than by the shape of the text it drew. That is
6
+ * what retires the old `frame.ts`, which recognised a box by counting `│` and
7
+ * had to exclude markdown tables by hand.
8
+ */
9
+
10
+ export type Rect = { x: number; y: number; width: number; height: number };
11
+
12
+ export type BoxLike = {
13
+ component?: unknown;
14
+ rect: Rect;
15
+ clip?: Rect;
16
+ children?: readonly BoxLike[];
17
+ };
18
+
19
+ export function rectContains(rect: Rect, x: number, y: number): boolean {
20
+ return x >= rect.x && x < rect.x + rect.width && y >= rect.y && y < rect.y + rect.height;
21
+ }
22
+
23
+ function visible(box: BoxLike, x: number, y: number): boolean {
24
+ if (!rectContains(box.rect, x, y)) return false;
25
+ // A box scrolled partly out of its container draws nothing outside the clip.
26
+ return box.clip ? rectContains(box.clip, x, y) : true;
27
+ }
28
+
29
+ export type HitTestOptions = {
30
+ /**
31
+ * Match on `rect` alone, ignoring `clip`.
32
+ *
33
+ * `clip` answers "is this cell painted right now" — pi-tui rewrites it on
34
+ * every frame to the intersection of the box with its scroll viewport
35
+ * (`updateClips` in `layout.js`), while `rect` stays in the content's own
36
+ * coordinates. So a question asked in *screen* space (which box drew the
37
+ * cell under the pointer) must honour it, and a question asked in
38
+ * *content* space (which component owns content row N of a scroll view)
39
+ * must not: content row N belongs to the same component whether or not the
40
+ * viewport happens to be showing it.
41
+ *
42
+ * Frame ownership used to be the caller; it moved to `component-tree.ts`,
43
+ * where the distinction cannot arise at all because a component graph has
44
+ * no clip to consult. This stays for the next content-space question asked
45
+ * of the layout tree — the distinction is a property of `clip`, not of the
46
+ * caller that first ran into it.
47
+ */
48
+ ignoreClip?: boolean;
49
+ };
50
+
51
+ /** Root first, innermost last. Empty when the point is outside the root. */
52
+ export function boxesAt(root: BoxLike, x: number, y: number, options?: HitTestOptions): BoxLike[] {
53
+ const hit = options?.ignoreClip
54
+ ? (box: BoxLike) => rectContains(box.rect, x, y)
55
+ : (box: BoxLike) => visible(box, x, y);
56
+ const path: BoxLike[] = [];
57
+ let current: BoxLike | undefined = root;
58
+ while (current && hit(current)) {
59
+ path.push(current);
60
+ current = current.children?.find(hit);
61
+ }
62
+ return path;
63
+ }
64
+
65
+ /**
66
+ * The first box, depth first, whose component satisfies `predicate`.
67
+ *
68
+ * `boxFor` asks "which box is this component's", which is the question when
69
+ * the component is itself a layout node. It is not always: a component with no
70
+ * `LAYOUT_NODE` is folded into its parent's leaf box, so the box that *covers*
71
+ * it belongs to an ancestor and the only way to recognise it is through that
72
+ * ancestor. `editor-mouse.ts` is the caller that needs the looser question.
73
+ */
74
+ export function boxMatching(
75
+ root: BoxLike | undefined,
76
+ predicate: (component: unknown) => boolean,
77
+ ): BoxLike | undefined {
78
+ if (!root) return undefined;
79
+ if (predicate(root.component)) return root;
80
+ for (const child of root.children ?? []) {
81
+ const found = boxMatching(child, predicate);
82
+ if (found) return found;
83
+ }
84
+ return undefined;
85
+ }
86
+
87
+ export function boxFor(root: BoxLike, component: unknown): BoxLike | undefined {
88
+ return boxMatching(root, (candidate) => candidate === component);
89
+ }
90
+
91
+ export type ScrollBoxLike = BoxLike & {
92
+ scrollView?: unknown;
93
+ scrollContentLines?: readonly string[];
94
+ };
95
+
96
+ /**
97
+ * The lines behind a scroll view, found by walking the layout tree. This is
98
+ * `getScrollViewBox`'s own logic from `layout.js`, mirrored here in miniature
99
+ * because it is not exported from pi-tui's published entry point.
100
+ */
101
+ export function scrollContentLinesFor(
102
+ root: ScrollBoxLike | undefined,
103
+ scrollView: unknown,
104
+ ): readonly string[] | undefined {
105
+ if (!root) return undefined;
106
+ if (root.scrollView === scrollView) return root.scrollContentLines;
107
+ for (const child of root.children ?? []) {
108
+ const found = scrollContentLinesFor(child as ScrollBoxLike, scrollView);
109
+ if (found) return found;
110
+ }
111
+ return undefined;
112
+ }
113
+
114
+ /**
115
+ * The box holding a scroll view's content — the same walk
116
+ * `scrollContentLinesFor` does, returning the box rather than its rows. Two
117
+ * things come off it that the lines alone cannot answer:
118
+ *
119
+ * - `rect.width` — the width its component was rendered at, which is what
120
+ * `createComponentTree` has to be given to reproduce the same row heights
121
+ * (`layoutComponent`'s "scroll" branch renders the content at
122
+ * `node.state.getContentWidth(width)` and sets `rect.width` to it).
123
+ * - `rect.y` — where content row 0 sits on screen, for a caller asking in
124
+ * screen coordinates. It is `viewportY - scrollTop`: the "scroll" branch
125
+ * lays the child out at `y - scrollTop` and then translates it back by the
126
+ * same amount. That relation is pinned in
127
+ * `test/contract/transcript-layout.test.ts`.
128
+ *
129
+ * Like the rest of this module it currently has no production caller — the
130
+ * screen-row-to-content-row conversion is what Task 8's click-to-expand needs
131
+ * to find the component under a click.
132
+ */
133
+ export function scrollContentOrigin(
134
+ root: BoxLike | undefined,
135
+ scrollView: unknown,
136
+ ): BoxLike | undefined {
137
+ if (!root) return undefined;
138
+ if ((root as ScrollBoxLike).scrollView === scrollView) return root.children?.[0];
139
+ for (const child of root.children ?? []) {
140
+ const found = scrollContentOrigin(child, scrollView);
141
+ if (found) return found;
142
+ }
143
+ return undefined;
144
+ }