@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.
- package/LICENSE +22 -0
- package/README.md +223 -0
- package/extensions/starline/config.ts +1317 -0
- package/extensions/starline/editor-cursor.ts +55 -0
- package/extensions/starline/editor-factory-marker.ts +49 -0
- package/extensions/starline/editor-metadata-format.ts +259 -0
- package/extensions/starline/extension-status.ts +88 -0
- package/extensions/starline/footer-format.ts +279 -0
- package/extensions/starline/footer.ts +744 -0
- package/extensions/starline/format.ts +473 -0
- package/extensions/starline/git.ts +467 -0
- package/extensions/starline/icons.ts +294 -0
- package/extensions/starline/index.ts +651 -0
- package/extensions/starline/live-context.ts +75 -0
- package/extensions/starline/mouse/capabilities.ts +207 -0
- package/extensions/starline/mouse/component-tree.ts +230 -0
- package/extensions/starline/mouse/editor-caret.ts +498 -0
- package/extensions/starline/mouse/editor-hit-test.ts +133 -0
- package/extensions/starline/mouse/editor-mouse.ts +142 -0
- package/extensions/starline/mouse/editor-scroll.ts +119 -0
- package/extensions/starline/mouse/editor-text-cursor.ts +209 -0
- package/extensions/starline/mouse/editor-text-edit.ts +90 -0
- package/extensions/starline/mouse/hint.ts +14 -0
- package/extensions/starline/mouse/hit-test.ts +144 -0
- package/extensions/starline/mouse/index.ts +1119 -0
- package/extensions/starline/mouse/selection-state.ts +68 -0
- package/extensions/starline/mouse/tool-box.ts +290 -0
- package/extensions/starline/mouse/transcript-copy.ts +166 -0
- package/extensions/starline/mouse/word-select.ts +145 -0
- package/extensions/starline/package-version.ts +650 -0
- package/extensions/starline/paste-collapse.ts +312 -0
- package/extensions/starline/pill-config.ts +122 -0
- package/extensions/starline/pill.ts +295 -0
- package/extensions/starline/project-refresh.ts +104 -0
- package/extensions/starline/project-state.ts +66 -0
- package/extensions/starline/prototype-patch-registry.ts +136 -0
- package/extensions/starline/runtime.ts +841 -0
- package/extensions/starline/selector-border.ts +77 -0
- package/extensions/starline/session-lifecycle.ts +60 -0
- package/extensions/starline/settings-command.ts +925 -0
- package/extensions/starline/state.ts +65 -0
- package/extensions/starline/style.ts +528 -0
- package/extensions/starline/ui.ts +611 -0
- package/extensions/starline/user-message.ts +267 -0
- 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
|
+
}
|