@gavin-hu/my-pi-agent 0.16.0 → 0.18.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/README.md +29 -15
- package/README.zh-CN.md +27 -15
- package/extensions/bytefly/README.md +195 -115
- package/extensions/bytefly/footer/collect/entries.ts +1 -6
- package/extensions/bytefly/footer/collect/snapshot.ts +23 -9
- package/extensions/bytefly/footer/price/price.ts +12 -8
- package/extensions/bytefly/footer/register.ts +13 -1
- package/extensions/bytefly/footer/types.ts +11 -0
- package/extensions/bytefly/footer/unpriced.ts +36 -0
- package/extensions/bytefly/header/component.ts +15 -29
- package/extensions/bytefly/header/identity.ts +71 -0
- package/extensions/bytefly/header/register.ts +9 -3
- package/extensions/bytefly/index.ts +13 -5
- package/extensions/bytefly/message/format.ts +20 -13
- package/extensions/bytefly/message/register.ts +8 -7
- package/extensions/bytefly/notify/types.ts +1 -1
- package/extensions/bytefly/panel/commands.ts +46 -25
- package/extensions/bytefly/panel/format.ts +49 -14
- package/extensions/bytefly/panel/join.ts +35 -9
- package/extensions/bytefly/panel/settings.ts +29 -39
- package/extensions/bytefly/panel/skills.ts +51 -0
- package/extensions/bytefly/panel/tui/extensions-view.ts +7 -61
- package/extensions/bytefly/panel/tui/frame.ts +9 -1
- package/extensions/bytefly/panel/tui/list-view.ts +72 -0
- package/extensions/bytefly/panel/tui/nav.ts +71 -3
- package/extensions/bytefly/panel/tui/settings-section.ts +250 -0
- package/extensions/bytefly/panel/tui/skills-view.ts +50 -0
- package/extensions/bytefly/panel/tui.ts +223 -331
- package/extensions/bytefly/panel/types.ts +26 -8
- package/extensions/bytefly/picker/discovery.ts +32 -27
- package/extensions/bytefly/picker/resources.ts +16 -8
- package/extensions/bytefly/picker/runtime.ts +62 -34
- package/extensions/bytefly/picker/toggle.ts +16 -10
- package/extensions/bytefly/picker/types.ts +31 -13
- package/extensions/bytefly/update/format.ts +66 -8
- package/extensions/bytefly/update/patch.ts +66 -0
- package/extensions/bytefly/update/register.ts +4 -11
- package/extensions/bytefly/update/release.ts +65 -0
- package/extensions/bytefly/update/state.ts +14 -0
- package/extensions/bytefly/update/suppress.ts +47 -29
- package/extensions/bytefly/update/types.ts +15 -5
- package/extensions/check/tui.ts +2 -0
- package/extensions/context/README.md +21 -11
- package/extensions/context/format.ts +10 -8
- package/extensions/context/report.ts +21 -8
- package/extensions/context/tui.ts +26 -10
- package/extensions/context/types.ts +5 -3
- package/extensions/file-browse/README.md +89 -22
- package/extensions/file-browse/chrome.ts +83 -0
- package/extensions/file-browse/client.ts +74 -459
- package/extensions/file-browse/config.ts +12 -3
- package/extensions/file-browse/diff-html.ts +94 -0
- package/extensions/file-browse/html.ts +26 -152
- package/extensions/file-browse/live-client.ts +47 -1
- package/extensions/file-browse/live.ts +35 -14
- package/extensions/file-browse/palette.ts +224 -0
- package/extensions/file-browse/rev.ts +42 -0
- package/extensions/file-browse/router.ts +27 -6
- package/extensions/file-browse/server.ts +7 -0
- package/extensions/file-browse/styles.ts +10 -3
- package/extensions/file-browse/tree.ts +381 -0
- package/extensions/file-read/README.md +4 -1
- package/extensions/file-read/extract/odt.ts +7 -5
- package/extensions/goal/plan-route.ts +8 -2
- package/extensions/goal/tools.ts +6 -11
- package/extensions/job/README.md +7 -5
- package/extensions/job/index.ts +35 -52
- package/extensions/job/tools.ts +51 -77
- package/extensions/memory/README.md +3 -3
- package/extensions/memory/tools.ts +4 -6
- package/extensions/memory/tui.ts +5 -1
- package/extensions/plan/README.md +333 -84
- package/extensions/plan/commands.ts +73 -7
- package/extensions/plan/feedback.ts +300 -63
- package/extensions/plan/index.ts +1 -1
- package/extensions/plan/plan-index.ts +4 -2
- package/extensions/plan/plan-text.ts +18 -1
- package/extensions/plan/plans.ts +184 -58
- package/extensions/plan/review-prompt.ts +93 -0
- package/extensions/plan/review.ts +95 -95
- package/extensions/plan/runtime.ts +4 -2
- package/extensions/plan/split.ts +87 -0
- package/extensions/plan/steps.ts +123 -107
- package/extensions/plan/tools/cancel.ts +79 -0
- package/extensions/plan/tools/enter.ts +17 -12
- package/extensions/plan/tools/exit.ts +148 -59
- package/extensions/plan/tools/steps.ts +7 -19
- package/extensions/plan/tools/write.ts +29 -31
- package/extensions/plan/tools.ts +6 -3
- package/extensions/plan/tui.ts +60 -15
- package/extensions/plan/types.ts +45 -3
- package/extensions/plan/web-chrome.ts +102 -0
- package/extensions/plan/web-client.ts +177 -446
- package/extensions/plan/web-collab.ts +207 -0
- package/extensions/plan/web-comments.ts +269 -0
- package/extensions/plan/web-markdown.ts +222 -0
- package/extensions/plan/web-page.ts +131 -19
- package/extensions/plan/web-router.ts +148 -32
- package/extensions/plan/web-share-session.ts +197 -0
- package/extensions/plan/web-share.ts +169 -0
- package/extensions/plan/web-streams.ts +124 -0
- package/extensions/plan/web-tokens.ts +63 -0
- package/extensions/plan/web.ts +193 -45
- package/extensions/question/README.md +3 -0
- package/extensions/question/dialogs.ts +91 -1
- package/extensions/question/tools.ts +2 -1
- package/extensions/rewind/checklist.ts +4 -0
- package/extensions/rewind/review-tui.ts +3 -1
- package/extensions/rewind/rewind-tui.ts +4 -0
- package/extensions/session-bus/README.md +247 -0
- package/extensions/session-bus/commands.ts +165 -0
- package/extensions/session-bus/format.ts +124 -0
- package/extensions/session-bus/index.ts +69 -0
- package/extensions/session-bus/notice.ts +58 -0
- package/extensions/session-bus/protocol.ts +134 -0
- package/extensions/session-bus/result.ts +243 -0
- package/extensions/session-bus/rows.ts +169 -0
- package/extensions/session-bus/runtime.ts +575 -0
- package/extensions/session-bus/store.ts +329 -0
- package/extensions/session-bus/team.ts +102 -0
- package/extensions/session-bus/tools.ts +227 -0
- package/extensions/session-bus/tui.ts +394 -0
- package/extensions/session-bus/types.ts +98 -0
- package/extensions/subagent/README.md +26 -13
- package/extensions/subagent/format.ts +72 -25
- package/extensions/subagent/index.ts +21 -47
- package/extensions/subagent/orchestrate.ts +18 -41
- package/extensions/subagent/render.ts +29 -42
- package/extensions/subagent/stream.ts +24 -18
- package/extensions/subagent/types.ts +7 -0
- package/extensions/todo/tools.ts +5 -6
- package/extensions/todo/tui.ts +5 -1
- package/extensions/watch/README.md +9 -5
- package/extensions/watch/index.ts +25 -44
- package/extensions/watch/tools.ts +31 -59
- package/extensions/wechat/README.md +45 -36
- package/extensions/wechat/bridge.ts +46 -48
- package/extensions/wechat/client.ts +1 -4
- package/extensions/wechat/commands.ts +3 -3
- package/extensions/wechat/deliver.ts +57 -37
- package/extensions/wechat/interaction.ts +54 -18
- package/extensions/wechat/login-screen.ts +2 -3
- package/extensions/wechat/notices.ts +9 -7
- package/extensions/wechat/peer-worker.ts +19 -34
- package/extensions/wechat/presence.ts +3 -4
- package/extensions/wechat/prompt.ts +53 -23
- package/extensions/wechat/reply-pipeline.ts +12 -4
- package/extensions/wechat/slash.ts +14 -0
- package/extensions/wechat/status-screen.ts +2 -2
- package/extensions/wechat/tools.ts +3 -11
- package/extensions/worktree/README.md +48 -12
- package/extensions/worktree/commands.ts +109 -65
- package/extensions/worktree/enter.ts +1 -1
- package/extensions/worktree/exit.ts +11 -1
- package/extensions/worktree/git.ts +5 -0
- package/extensions/worktree/lifecycle.ts +9 -1
- package/extensions/worktree/registry.ts +11 -5
- package/extensions/worktree/screen.ts +263 -0
- package/extensions/worktree/status.ts +84 -40
- package/lib/README.md +12 -7
- package/lib/claims/overlap.ts +43 -0
- package/lib/config.ts +12 -0
- package/lib/format.ts +6 -2
- package/lib/http/router.ts +40 -13
- package/lib/http/server.ts +27 -13
- package/lib/interaction.ts +28 -1
- package/lib/notice.ts +29 -2
- package/lib/plan-entry.ts +7 -2
- package/lib/plans/sections.ts +172 -0
- package/lib/plans/setting.ts +52 -0
- package/lib/plans/split.ts +215 -0
- package/lib/plans/store.ts +19 -0
- package/lib/plans/team.ts +151 -0
- package/lib/qr.ts +64 -0
- package/lib/repo-key.ts +74 -0
- package/lib/report.ts +117 -25
- package/lib/requirements.ts +1 -2
- package/lib/tool-result.ts +54 -4
- package/lib/tui/README.md +30 -8
- package/lib/tui/chrome.ts +27 -6
- package/lib/tui/dock-screen.ts +91 -19
- package/lib/tui/gauge.ts +9 -5
- package/lib/tui/line-pane.ts +15 -4
- package/lib/tui/tool.ts +23 -4
- package/lib/usage.ts +26 -0
- package/lib/web/README.md +131 -0
- package/lib/web/base.ts +19 -2
- package/lib/web/components.ts +10 -6
- package/lib/web/layout.ts +2 -0
- package/lib/web/markup.ts +2 -1
- package/lib/web/runtime.ts +103 -25
- package/lib/web/tokens.ts +23 -7
- package/package.json +5 -2
- package/skills/changelog/SKILL.md +57 -45
- package/skills/init/SKILL.md +80 -45
- package/skills/init/references/agents-md-quality.md +26 -2
- package/skills/init/references/interview-questions.md +36 -14
- package/skills/next/SKILL.md +209 -0
- package/skills/parallel-dev/SKILL.md +150 -0
- package/extensions/wechat/qrcode.ts +0 -46
|
@@ -2,245 +2,25 @@
|
|
|
2
2
|
* The first-party browser script, served as `/app.js`.
|
|
3
3
|
*
|
|
4
4
|
* It is intentionally tiny and dependency-free: it only progressively enhances
|
|
5
|
-
* what the server already rendered
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
5
|
+
* what the server already rendered. The sidebar (filter, fold, lazy tree,
|
|
6
|
+
* keyboard) is `tree.ts` and the quick-open palette is `palette.ts`; both are
|
|
7
|
+
* fragments of this same IIFE, interpolated below. What stays here is the shell:
|
|
8
|
+
* sorting, wrapping, copying, the diff's hunk navigation, the reading progress,
|
|
9
|
+
* and the page's keymap. Navigation works without any of it.
|
|
9
10
|
*
|
|
10
11
|
* Kept as a string constant so the extension is a single self-contained module
|
|
11
12
|
* and `bun build --no-bundle` needs no separate asset.
|
|
12
13
|
*/
|
|
13
14
|
|
|
14
|
-
import { keysDialogScript,
|
|
15
|
+
import { keysDialogScript, THEME_TOGGLE_JS } from "../../lib/web/index.ts";
|
|
15
16
|
import { LIVE_CLIENT_JS } from "./live-client.ts";
|
|
17
|
+
import { PALETTE_CLIENT_JS } from "./palette.ts";
|
|
18
|
+
import { TREE_CLIENT_JS } from "./tree.ts";
|
|
16
19
|
|
|
17
20
|
export const CLIENT_JS = `(function () {
|
|
18
21
|
"use strict";
|
|
19
|
-
var tree = document.querySelector("nav.bf-tree");
|
|
20
|
-
var filter = document.getElementById("filter");
|
|
21
|
-
var foldAllButton = document.getElementById("fold-all");
|
|
22
22
|
var rows = Array.prototype.slice.call(document.querySelectorAll("table.bf-table tbody tr"));
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
function matches(text) {
|
|
26
|
-
return !query || text.toLowerCase().indexOf(query) !== -1;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
30
|
-
|
|
31
|
-
function svgIcon(entry) {
|
|
32
|
-
var svg = document.createElementNS(SVG_NS, "svg");
|
|
33
|
-
svg.setAttribute("class", "browse-cat " + (entry.colorClass || ""));
|
|
34
|
-
svg.setAttribute("aria-hidden", "true");
|
|
35
|
-
svg.setAttribute("title", entry.label || "");
|
|
36
|
-
var use = document.createElementNS(SVG_NS, "use");
|
|
37
|
-
use.setAttribute("href", entry.iconHref);
|
|
38
|
-
svg.appendChild(use);
|
|
39
|
-
return svg;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function buildNode(entry) {
|
|
43
|
-
var li = document.createElement("li");
|
|
44
|
-
var row = document.createElement("div");
|
|
45
|
-
row.className = "bf-tree__row";
|
|
46
|
-
row.setAttribute("data-path", entry.path || "");
|
|
47
|
-
var twisty = document.createElement(entry.isDir ? "button" : "span");
|
|
48
|
-
twisty.className = "bf-tree__tw";
|
|
49
|
-
row.appendChild(twisty);
|
|
50
|
-
row.appendChild(svgIcon(entry));
|
|
51
|
-
if (entry.isDir) {
|
|
52
|
-
li.dataset.open = "false";
|
|
53
|
-
twisty.classList.add("bf-tree__tw--toggle");
|
|
54
|
-
twisty.type = "button";
|
|
55
|
-
twisty.textContent = "\\u25B8";
|
|
56
|
-
twisty.setAttribute("aria-expanded", "false");
|
|
57
|
-
twisty.setAttribute("aria-label", entry.name);
|
|
58
|
-
var dir = document.createElement("a");
|
|
59
|
-
dir.href = entry.href;
|
|
60
|
-
dir.textContent = entry.name + "/";
|
|
61
|
-
row.appendChild(dir);
|
|
62
|
-
} else if (entry.broken) {
|
|
63
|
-
twisty.className = "bf-tree__tw bf-tree__tw--spacer";
|
|
64
|
-
twisty.setAttribute("aria-hidden", "true");
|
|
65
|
-
var broken = document.createElement("span");
|
|
66
|
-
broken.className = "browse-broken";
|
|
67
|
-
broken.title = "broken symbolic link";
|
|
68
|
-
broken.textContent = entry.name;
|
|
69
|
-
row.appendChild(broken);
|
|
70
|
-
var mark = document.createElement("span");
|
|
71
|
-
mark.className = "browse-link-mark";
|
|
72
|
-
mark.textContent = "\\u21A9";
|
|
73
|
-
row.appendChild(mark);
|
|
74
|
-
} else {
|
|
75
|
-
twisty.className = "bf-tree__tw bf-tree__tw--spacer";
|
|
76
|
-
twisty.setAttribute("aria-hidden", "true");
|
|
77
|
-
var file = document.createElement("a");
|
|
78
|
-
file.href = entry.href;
|
|
79
|
-
file.textContent = entry.name;
|
|
80
|
-
row.appendChild(file);
|
|
81
|
-
}
|
|
82
|
-
li.appendChild(row);
|
|
83
|
-
return li;
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function filterNode(li) {
|
|
87
|
-
var row = li.querySelector(":scope > .bf-tree__row");
|
|
88
|
-
var own = matches(row ? row.getAttribute("data-path") || row.textContent : "");
|
|
89
|
-
var childMatch = false;
|
|
90
|
-
Array.prototype.forEach.call(li.querySelectorAll(":scope > ul > li"), function (child) {
|
|
91
|
-
if (filterNode(child)) childMatch = true;
|
|
92
|
-
});
|
|
93
|
-
var visible = own || childMatch;
|
|
94
|
-
li.hidden = !visible;
|
|
95
|
-
return visible;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function applyFilter() {
|
|
99
|
-
var empty = document.getElementById("tree-empty");
|
|
100
|
-
var anyTree = false;
|
|
101
|
-
if (tree) {
|
|
102
|
-
Array.prototype.forEach.call(tree.querySelectorAll(":scope > ul > li"), function (li) {
|
|
103
|
-
if (filterNode(li)) anyTree = true;
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
if (empty) empty.hidden = !tree || anyTree;
|
|
107
|
-
rows.forEach(function (row) {
|
|
108
|
-
var path = row.getAttribute("data-path") || row.getAttribute("data-name") || "";
|
|
109
|
-
row.hidden = !matches(path);
|
|
110
|
-
});
|
|
111
|
-
// Lists are repainted too: while a query is active every list is shown, and
|
|
112
|
-
// clearing the query brings the folds back.
|
|
113
|
-
if (tree) Array.prototype.forEach.call(tree.querySelectorAll("li[data-open]"), paint);
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
if (filter) {
|
|
117
|
-
filter.addEventListener("input", function () {
|
|
118
|
-
query = filter.value.trim().toLowerCase();
|
|
119
|
-
applyFilter();
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
|
|
123
|
-
// Visibility is derived: the intent in data-open, opened up while a filter is
|
|
124
|
-
// active so a fold cannot hide a match.
|
|
125
|
-
function isOpen(li) {
|
|
126
|
-
return query !== "" || li.dataset.open === "true";
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
function paint(li) {
|
|
130
|
-
var row = li.querySelector(":scope > .bf-tree__row");
|
|
131
|
-
var twisty = row ? row.querySelector(":scope > .bf-tree__tw") : null;
|
|
132
|
-
if (!twisty || twisty.tagName !== "BUTTON") return;
|
|
133
|
-
var open = isOpen(li);
|
|
134
|
-
// A pending load owns the glyph until it lands or fails.
|
|
135
|
-
if (!li.dataset.loading && !li.dataset.error) {
|
|
136
|
-
twisty.textContent = open ? "\\u25BE" : "\\u25B8";
|
|
137
|
-
twisty.setAttribute("aria-expanded", open ? "true" : "false");
|
|
138
|
-
}
|
|
139
|
-
var list = li.querySelector(":scope > ul");
|
|
140
|
-
if (list) list.hidden = !open;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function setOpen(li, open) {
|
|
144
|
-
li.dataset.open = open ? "true" : "false";
|
|
145
|
-
paint(li);
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
// Every dir with rendered children: what the fold-all control acts on.
|
|
149
|
-
function foldableDirs() {
|
|
150
|
-
var dirs = [];
|
|
151
|
-
if (!tree) return dirs;
|
|
152
|
-
Array.prototype.forEach.call(tree.querySelectorAll("li"), function (li) {
|
|
153
|
-
if (li.querySelector(":scope > ul")) dirs.push(li);
|
|
154
|
-
});
|
|
155
|
-
return dirs;
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function syncFoldButton() {
|
|
159
|
-
if (!foldAllButton) return;
|
|
160
|
-
var dirs = foldableDirs();
|
|
161
|
-
var open = 0;
|
|
162
|
-
dirs.forEach(function (li) {
|
|
163
|
-
if (li.dataset.open === "true") open++;
|
|
164
|
-
});
|
|
165
|
-
foldAllButton.hidden = dirs.length === 0;
|
|
166
|
-
foldAllButton.textContent = open > 0 ? "fold all" : "expand all";
|
|
167
|
-
}
|
|
168
|
-
|
|
169
|
-
function setAll(open) {
|
|
170
|
-
foldableDirs().forEach(function (li) {
|
|
171
|
-
setOpen(li, open);
|
|
172
|
-
});
|
|
173
|
-
syncFoldButton();
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
function loadChildren(li, twisty, path) {
|
|
177
|
-
var name = twisty.getAttribute("aria-label") || "";
|
|
178
|
-
li.dataset.loading = "true";
|
|
179
|
-
twisty.textContent = "\\u2026";
|
|
180
|
-
twisty.setAttribute("aria-busy", "true");
|
|
181
|
-
twisty.setAttribute("aria-label", "Loading " + name);
|
|
182
|
-
fetch("/api/tree?path=" + encodeURIComponent(path))
|
|
183
|
-
.then(function (response) {
|
|
184
|
-
if (!response.ok) throw new Error("load failed");
|
|
185
|
-
return response.json();
|
|
186
|
-
})
|
|
187
|
-
.then(function (data) {
|
|
188
|
-
var built = document.createElement("ul");
|
|
189
|
-
(data.entries || []).forEach(function (entry) {
|
|
190
|
-
built.appendChild(buildNode(entry));
|
|
191
|
-
});
|
|
192
|
-
li.appendChild(built);
|
|
193
|
-
delete li.dataset.loading;
|
|
194
|
-
delete li.dataset.error;
|
|
195
|
-
setOpen(li, true);
|
|
196
|
-
twisty.setAttribute("aria-busy", "false");
|
|
197
|
-
twisty.setAttribute("aria-label", name);
|
|
198
|
-
applyFilter();
|
|
199
|
-
})
|
|
200
|
-
.catch(function () {
|
|
201
|
-
delete li.dataset.loading;
|
|
202
|
-
li.dataset.error = "true";
|
|
203
|
-
li.dataset.open = "false";
|
|
204
|
-
twisty.textContent = "\\u26A0";
|
|
205
|
-
twisty.setAttribute("aria-busy", "false");
|
|
206
|
-
twisty.setAttribute("aria-expanded", "false");
|
|
207
|
-
twisty.setAttribute("aria-label", "Could not load " + name + ". Press to retry.");
|
|
208
|
-
});
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
if (tree) {
|
|
212
|
-
tree.addEventListener("click", function (event) {
|
|
213
|
-
var twisty = event.target.closest(".bf-tree__tw");
|
|
214
|
-
if (!twisty || twisty.tagName !== "BUTTON" || !tree.contains(twisty)) return;
|
|
215
|
-
event.preventDefault();
|
|
216
|
-
var li = twisty.closest("li");
|
|
217
|
-
if (!li) return;
|
|
218
|
-
var list = li.querySelector(":scope > ul");
|
|
219
|
-
if (li.dataset.open === "true") {
|
|
220
|
-
setOpen(li, false);
|
|
221
|
-
return;
|
|
222
|
-
}
|
|
223
|
-
if (list) {
|
|
224
|
-
setOpen(li, true);
|
|
225
|
-
applyFilter();
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
var row = li.querySelector(":scope > .bf-tree__row");
|
|
229
|
-
var path = row ? row.getAttribute("data-path") : null;
|
|
230
|
-
if (!path) return;
|
|
231
|
-
loadChildren(li, twisty, path);
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
|
|
235
|
-
var panel = document.querySelector("details.bf-app__sidebar");
|
|
236
|
-
if (panel && window.matchMedia) {
|
|
237
|
-
var mq = window.matchMedia("(max-width: ${SIDEBAR_COLLAPSE_MAX}px)");
|
|
238
|
-
var syncSide = function () { panel.open = !mq.matches; };
|
|
239
|
-
syncSide();
|
|
240
|
-
if (mq.addEventListener) mq.addEventListener("change", syncSide);
|
|
241
|
-
else if (mq.addListener) mq.addListener(syncSide);
|
|
242
|
-
}
|
|
243
|
-
|
|
23
|
+
${TREE_CLIENT_JS}
|
|
244
24
|
Array.prototype.forEach.call(document.querySelectorAll("img.bf-table__thumb"), function (img) {
|
|
245
25
|
img.addEventListener("error", function () {
|
|
246
26
|
img.hidden = true;
|
|
@@ -366,213 +146,65 @@ export const CLIENT_JS = `(function () {
|
|
|
366
146
|
});
|
|
367
147
|
});
|
|
368
148
|
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
var
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
return path
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
function
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
var
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
paletteList.id = "palette-list";
|
|
419
|
-
paletteList.setAttribute("role", "listbox");
|
|
420
|
-
paletteStatus = document.createElement("p");
|
|
421
|
-
paletteStatus.className = "bf-dialog__status";
|
|
422
|
-
paletteStatus.setAttribute("role", "status");
|
|
423
|
-
dialog.appendChild(paletteInput);
|
|
424
|
-
dialog.appendChild(paletteList);
|
|
425
|
-
dialog.appendChild(paletteStatus);
|
|
426
|
-
paletteBackdrop.appendChild(dialog);
|
|
427
|
-
document.body.appendChild(paletteBackdrop);
|
|
428
|
-
|
|
429
|
-
paletteBackdrop.addEventListener("mousedown", function (event) {
|
|
430
|
-
if (event.target === paletteBackdrop) closePalette();
|
|
431
|
-
});
|
|
432
|
-
paletteInput.addEventListener("input", function () {
|
|
433
|
-
if (paletteTimer) window.clearTimeout(paletteTimer);
|
|
434
|
-
paletteTimer = window.setTimeout(runSearch, 80);
|
|
435
|
-
});
|
|
436
|
-
paletteInput.addEventListener("keydown", function (event) {
|
|
437
|
-
if (event.key === "Tab") {
|
|
438
|
-
event.preventDefault();
|
|
439
|
-
} else if (event.key === "ArrowDown" || (event.ctrlKey && event.key === "n")) {
|
|
440
|
-
event.preventDefault();
|
|
441
|
-
moveActive(1);
|
|
442
|
-
} else if (event.key === "ArrowUp" || (event.ctrlKey && event.key === "p")) {
|
|
443
|
-
event.preventDefault();
|
|
444
|
-
moveActive(-1);
|
|
445
|
-
} else if (event.key === "Enter") {
|
|
446
|
-
event.preventDefault();
|
|
447
|
-
openActive();
|
|
448
|
-
} else if (event.key === "Escape") {
|
|
449
|
-
event.preventDefault();
|
|
450
|
-
closePalette();
|
|
451
|
-
}
|
|
452
|
-
});
|
|
453
|
-
paletteList.addEventListener("mousemove", function (event) {
|
|
454
|
-
var option = closestRole(event.target, "option");
|
|
455
|
-
if (option) setActive(Number(option.getAttribute("data-index")));
|
|
456
|
-
});
|
|
457
|
-
paletteList.addEventListener("click", function (event) {
|
|
458
|
-
var option = closestRole(event.target, "option");
|
|
459
|
-
if (option) {
|
|
460
|
-
setActive(Number(option.getAttribute("data-index")));
|
|
461
|
-
openActive();
|
|
462
|
-
}
|
|
463
|
-
});
|
|
464
|
-
}
|
|
465
|
-
|
|
466
|
-
function openPalette() {
|
|
467
|
-
buildPalette();
|
|
468
|
-
paletteBackdrop.hidden = false;
|
|
469
|
-
document.body.classList.add("palette-open");
|
|
470
|
-
paletteInput.value = "";
|
|
471
|
-
renderResults([], "Searching…");
|
|
472
|
-
paletteInput.focus();
|
|
473
|
-
runSearch();
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
function closePalette() {
|
|
477
|
-
if (!paletteBackdrop) return;
|
|
478
|
-
paletteBackdrop.hidden = true;
|
|
479
|
-
document.body.classList.remove("palette-open");
|
|
480
|
-
if (quickOpenButton) quickOpenButton.focus();
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
function runSearch() {
|
|
484
|
-
var query = paletteInput.value.trim();
|
|
485
|
-
fetch("/api/files?q=" + encodeURIComponent(query))
|
|
486
|
-
.then(function (response) {
|
|
487
|
-
if (!response.ok) throw new Error("search failed");
|
|
488
|
-
return response.json();
|
|
489
|
-
})
|
|
490
|
-
.then(function (data) {
|
|
491
|
-
if (paletteInput.value.trim() !== query) return;
|
|
492
|
-
if (query === "") {
|
|
493
|
-
renderResults([], data.total + " files indexed" + (data.truncated ? " (truncated)" : ""));
|
|
494
|
-
} else {
|
|
495
|
-
renderResults(data.results || [], null);
|
|
496
|
-
}
|
|
497
|
-
})
|
|
498
|
-
.catch(function () {
|
|
499
|
-
renderResults([], "Search failed");
|
|
500
|
-
});
|
|
501
|
-
}
|
|
502
|
-
|
|
503
|
-
function appendHighlighted(target, path, positions) {
|
|
504
|
-
var marks = {};
|
|
505
|
-
(positions || []).forEach(function (index) { marks[index] = true; });
|
|
506
|
-
var buffer = "";
|
|
507
|
-
var marked = false;
|
|
508
|
-
function flush() {
|
|
509
|
-
if (buffer === "") return;
|
|
510
|
-
if (marked) {
|
|
511
|
-
var mark = document.createElement("mark");
|
|
512
|
-
mark.textContent = buffer;
|
|
513
|
-
target.appendChild(mark);
|
|
514
|
-
} else {
|
|
515
|
-
target.appendChild(document.createTextNode(buffer));
|
|
516
|
-
}
|
|
517
|
-
buffer = "";
|
|
518
|
-
}
|
|
519
|
-
for (var i = 0; i < path.length; i++) {
|
|
520
|
-
var isMatch = marks[i] === true;
|
|
521
|
-
if (isMatch !== marked) { flush(); marked = isMatch; }
|
|
522
|
-
buffer += path.charAt(i);
|
|
523
|
-
}
|
|
524
|
-
flush();
|
|
525
|
-
}
|
|
526
|
-
|
|
527
|
-
function renderResults(results, hint) {
|
|
528
|
-
paletteResults = results;
|
|
529
|
-
paletteActive = results.length > 0 ? 0 : -1;
|
|
530
|
-
paletteList.textContent = "";
|
|
531
|
-
results.forEach(function (result, index) {
|
|
532
|
-
var option = document.createElement("li");
|
|
533
|
-
option.className = "bf-dialog__option";
|
|
534
|
-
option.id = "palette-option-" + index;
|
|
535
|
-
option.setAttribute("role", "option");
|
|
536
|
-
option.setAttribute("data-index", String(index));
|
|
537
|
-
appendHighlighted(option, result.path, result.positions);
|
|
538
|
-
paletteList.appendChild(option);
|
|
539
|
-
});
|
|
540
|
-
if (hint) paletteStatus.textContent = hint;
|
|
541
|
-
else if (results.length === 0) paletteStatus.textContent = "No matches";
|
|
542
|
-
else paletteStatus.textContent = results.length + (results.length === 1 ? " match" : " matches");
|
|
543
|
-
updateActive();
|
|
544
|
-
}
|
|
545
|
-
|
|
546
|
-
function updateActive() {
|
|
547
|
-
Array.prototype.forEach.call(paletteList.children, function (option, index) {
|
|
548
|
-
option.setAttribute("aria-selected", index === paletteActive ? "true" : "false");
|
|
549
|
-
});
|
|
550
|
-
var active = paletteList.children[paletteActive];
|
|
551
|
-
if (paletteActive >= 0 && active) {
|
|
552
|
-
paletteInput.setAttribute("aria-activedescendant", "palette-option-" + paletteActive);
|
|
553
|
-
if (active.scrollIntoView) active.scrollIntoView({ block: "nearest" });
|
|
554
|
-
} else {
|
|
555
|
-
paletteInput.removeAttribute("aria-activedescendant");
|
|
149
|
+
${PALETTE_CLIENT_JS}
|
|
150
|
+
|
|
151
|
+
// A code reference: the file's path and the lines the reader selected. A click on
|
|
152
|
+
// the button collapses the selection before its click handler runs, so a new
|
|
153
|
+
// selection replaces the stored one and a collapse leaves it alone.
|
|
154
|
+
var reference = null;
|
|
155
|
+
|
|
156
|
+
function lineNode(node) {
|
|
157
|
+
var element = node && node.nodeType === 3 ? node.parentElement : node;
|
|
158
|
+
return element && element.closest ? element.closest(".bf-code__line") : null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function referenceText() {
|
|
162
|
+
if (!reference || !reference.path) return "";
|
|
163
|
+
var span = reference.start === reference.end ? String(reference.start) : reference.start + "-" + reference.end;
|
|
164
|
+
return reference.path + ":" + span;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function captureReference() {
|
|
168
|
+
var button = document.getElementById("copy-reference");
|
|
169
|
+
if (!button) return;
|
|
170
|
+
var selection = window.getSelection && window.getSelection();
|
|
171
|
+
if (!selection || selection.isCollapsed || selection.rangeCount === 0) return;
|
|
172
|
+
var range = selection.getRangeAt(0);
|
|
173
|
+
var from = lineNode(range.startContainer);
|
|
174
|
+
var to = lineNode(range.endContainer);
|
|
175
|
+
if (!from || !to) return;
|
|
176
|
+
var pre = from.closest("pre[data-path]");
|
|
177
|
+
if (!pre || pre !== to.closest("pre[data-path]")) return;
|
|
178
|
+
var lines = Array.prototype.slice.call(pre.querySelectorAll(".bf-code__line"));
|
|
179
|
+
var first = lines.indexOf(from);
|
|
180
|
+
var last = lines.indexOf(to);
|
|
181
|
+
if (first < 0 || last < 0) return;
|
|
182
|
+
reference = {
|
|
183
|
+
path: pre.getAttribute("data-path") || "",
|
|
184
|
+
start: Math.min(first, last) + 1,
|
|
185
|
+
end: Math.max(first, last) + 1
|
|
186
|
+
};
|
|
187
|
+
button.disabled = false;
|
|
188
|
+
button.title = "Copy " + referenceText();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function copyReference() {
|
|
192
|
+
var text = referenceText();
|
|
193
|
+
var button = document.getElementById("copy-reference");
|
|
194
|
+
if (!text) {
|
|
195
|
+
var status = document.getElementById("copy-status");
|
|
196
|
+
if (status) status.textContent = "Select lines in the code to copy a reference.";
|
|
197
|
+
return;
|
|
556
198
|
}
|
|
199
|
+
copyText(text)
|
|
200
|
+
.then(function () { if (button) flash(button, "copied", "Copied " + text); })
|
|
201
|
+
.catch(function () { if (button) flash(button, "failed", "Copy failed"); });
|
|
557
202
|
}
|
|
558
203
|
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
}
|
|
564
|
-
|
|
565
|
-
function moveActive(delta) {
|
|
566
|
-
if (paletteResults.length === 0) return;
|
|
567
|
-
var next = paletteActive + delta;
|
|
568
|
-
if (next < 0) next = paletteResults.length - 1;
|
|
569
|
-
if (next >= paletteResults.length) next = 0;
|
|
570
|
-
setActive(next);
|
|
571
|
-
}
|
|
572
|
-
|
|
573
|
-
function openActive() {
|
|
574
|
-
var result = paletteResults[paletteActive];
|
|
575
|
-
if (result) window.location.assign("/view/" + encodeSegments(result.path));
|
|
204
|
+
if (document.getElementById("copy-reference")) {
|
|
205
|
+
document.getElementById("copy-reference").addEventListener("click", copyReference);
|
|
206
|
+
document.addEventListener("mouseup", captureReference);
|
|
207
|
+
document.addEventListener("keyup", captureReference);
|
|
576
208
|
}
|
|
577
209
|
|
|
578
210
|
// Keyboard: the shared shortcut sheet, the per-page actions the server
|
|
@@ -657,15 +289,10 @@ export const CLIENT_JS = `(function () {
|
|
|
657
289
|
markTableOverflow();
|
|
658
290
|
|
|
659
291
|
document.addEventListener("keydown", function (event) {
|
|
660
|
-
if (
|
|
661
|
-
event.preventDefault();
|
|
662
|
-
window.bfKeys.close();
|
|
663
|
-
return;
|
|
664
|
-
}
|
|
665
|
-
if (paletteOpen()) {
|
|
292
|
+
if (window.bfPalette && window.bfPalette.isOpen()) {
|
|
666
293
|
if (event.key === "Escape") {
|
|
667
294
|
event.preventDefault();
|
|
668
|
-
|
|
295
|
+
window.bfPalette.close();
|
|
669
296
|
}
|
|
670
297
|
return;
|
|
671
298
|
}
|
|
@@ -675,19 +302,22 @@ export const CLIENT_JS = `(function () {
|
|
|
675
302
|
if (editable) return;
|
|
676
303
|
if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === "k") {
|
|
677
304
|
event.preventDefault();
|
|
678
|
-
|
|
305
|
+
window.bfPalette.open();
|
|
679
306
|
return;
|
|
680
307
|
}
|
|
681
308
|
if (event.metaKey || event.ctrlKey || event.altKey) return;
|
|
682
309
|
if (event.key === "/") {
|
|
683
310
|
event.preventDefault();
|
|
684
|
-
|
|
311
|
+
window.bfPalette.open();
|
|
685
312
|
} else if (event.key === "?") {
|
|
686
313
|
event.preventDefault();
|
|
687
314
|
window.bfKeys.toggle();
|
|
688
315
|
} else if (event.key === "u" && wrapButton) {
|
|
689
316
|
event.preventDefault();
|
|
690
317
|
wrapButton.click();
|
|
318
|
+
} else if (event.key === "y" && document.getElementById("copy-reference")) {
|
|
319
|
+
event.preventDefault();
|
|
320
|
+
copyReference();
|
|
691
321
|
} else if (event.key === "r" && document.querySelector(".bf-card__actions .bf-segmented")) {
|
|
692
322
|
event.preventDefault();
|
|
693
323
|
toggleRaw();
|
|
@@ -697,26 +327,11 @@ export const CLIENT_JS = `(function () {
|
|
|
697
327
|
} else if (event.key === "p" && hunkAnchors.length > 0) {
|
|
698
328
|
event.preventDefault();
|
|
699
329
|
moveHunk(-1);
|
|
700
|
-
} else if (event.key === "-" &&
|
|
330
|
+
} else if (event.key === "-" && document.getElementById("fold-all")) {
|
|
701
331
|
event.preventDefault();
|
|
702
|
-
|
|
332
|
+
document.getElementById("fold-all").click();
|
|
703
333
|
}
|
|
704
334
|
});
|
|
705
335
|
|
|
706
|
-
if (quickOpenButton) quickOpenButton.addEventListener("click", openPalette);
|
|
707
|
-
if (foldAllButton) {
|
|
708
|
-
foldAllButton.addEventListener("click", function () {
|
|
709
|
-
// The control folds when anything is open, and expands when nothing is.
|
|
710
|
-
var open = false;
|
|
711
|
-
foldableDirs().forEach(function (li) {
|
|
712
|
-
if (li.dataset.open === "true") open = true;
|
|
713
|
-
});
|
|
714
|
-
setAll(!open);
|
|
715
|
-
});
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
// The control starts in the state the rendered tree implies.
|
|
719
|
-
syncFoldButton();
|
|
720
|
-
|
|
721
336
|
${LIVE_CLIENT_JS}})();
|
|
722
337
|
`;
|
|
@@ -57,7 +57,7 @@ export const DEFAULT_CONFIG: ServeConfig = {
|
|
|
57
57
|
diffContext: 3,
|
|
58
58
|
maxDiffLines: 4000,
|
|
59
59
|
live: true,
|
|
60
|
-
liveIntervalMs:
|
|
60
|
+
liveIntervalMs: 5000,
|
|
61
61
|
};
|
|
62
62
|
|
|
63
63
|
/** Base of the per-session port range. */
|
|
@@ -66,8 +66,8 @@ export const PORT_BASE = 4780;
|
|
|
66
66
|
/** Number of ports in the per-session range. */
|
|
67
67
|
export const PORT_RANGE = 1000;
|
|
68
68
|
|
|
69
|
-
/** Fastest live-status cadence a page may be given
|
|
70
|
-
*
|
|
69
|
+
/** Fastest live-status cadence a page may be given: short enough to watch, and a
|
|
70
|
+
* floor so a page cannot outrun the git reads behind each frame. */
|
|
71
71
|
export const LIVE_INTERVAL_MIN = 250;
|
|
72
72
|
|
|
73
73
|
/** Slowest live-status cadence, so a page cannot look abandoned. */
|
|
@@ -81,6 +81,15 @@ export const LIVE_INTERVAL_MAX = 60_000;
|
|
|
81
81
|
*/
|
|
82
82
|
export const IDLE_STOP_MS = 10_000;
|
|
83
83
|
|
|
84
|
+
/**
|
|
85
|
+
* How many pages may hold the status stream at once. One snapshot serves them
|
|
86
|
+
* all, but every attached page keeps the ticker — and the git read behind each
|
|
87
|
+
* frame — running, so a local page cannot open a thousand streams and pin the
|
|
88
|
+
* server alive. The refusal is a `204`, which tells an event source to stop
|
|
89
|
+
* reconnecting instead of retrying for a slot that will not open.
|
|
90
|
+
*/
|
|
91
|
+
export const LIVE_MAX_CLIENTS = 8;
|
|
92
|
+
|
|
84
93
|
/** A random port in the per-session range `PORT_BASE`–`PORT_BASE + PORT_RANGE - 1`. */
|
|
85
94
|
export function randomPort(): number {
|
|
86
95
|
return PORT_BASE + randomInt(PORT_RANGE);
|