@gavin-hu/my-pi-agent 0.19.0 → 0.21.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 +19 -12
- package/README.zh-CN.md +15 -9
- package/docs/extensions/README.md +2 -1
- package/docs/extensions/board.md +329 -151
- package/docs/extensions/bytefly.md +427 -273
- package/docs/extensions/file-browse.md +46 -44
- package/docs/extensions/job.md +1 -2
- package/docs/extensions/plan.md +86 -24
- package/docs/extensions/serve.md +129 -0
- package/docs/extensions/session-bus.md +23 -10
- package/docs/extensions/team.md +56 -28
- package/docs/extensions/wechat.md +2 -1
- package/docs/extensions/worktree.md +30 -15
- package/extensions/board/commands.ts +48 -17
- package/extensions/board/format.ts +107 -9
- package/extensions/board/index.ts +14 -2
- package/extensions/board/join.ts +31 -10
- package/extensions/board/page.ts +202 -156
- package/extensions/board/result.ts +10 -24
- package/extensions/board/rows.ts +1 -1
- package/extensions/board/schema.ts +31 -10
- package/extensions/board/seat.ts +9 -10
- package/extensions/board/snapshot.ts +7 -5
- package/extensions/board/tools.ts +211 -34
- package/extensions/board/transport/router.ts +27 -110
- package/extensions/board/transport/server.ts +76 -56
- package/extensions/board/types.ts +57 -0
- package/extensions/board/view/client.ts +24 -13
- package/extensions/board/view/dialog.ts +43 -0
- package/extensions/board/view/keys.ts +28 -24
- package/extensions/board/view/live.ts +6 -178
- package/extensions/board/view/names.ts +91 -0
- package/extensions/board/view/page.ts +60 -89
- package/extensions/board/view/render.ts +228 -200
- package/extensions/board/view/styles.ts +72 -49
- package/extensions/board/view/team.ts +63 -41
- package/extensions/bytefly/footer/budget.ts +6 -5
- package/extensions/bytefly/footer/collect/entries.ts +12 -4
- package/extensions/bytefly/footer/collect/snapshot.ts +8 -17
- package/extensions/bytefly/footer/config.ts +19 -11
- package/extensions/bytefly/footer/price/price.ts +30 -8
- package/extensions/bytefly/footer/register.ts +5 -5
- package/extensions/bytefly/footer/render/footer.ts +18 -4
- package/extensions/bytefly/footer/render/format.ts +18 -0
- package/extensions/bytefly/footer/render/key.ts +46 -0
- package/extensions/bytefly/footer/render/layout.ts +13 -3
- package/extensions/bytefly/footer/render/lines.ts +23 -7
- package/extensions/bytefly/footer/render/segments.ts +93 -31
- package/extensions/bytefly/footer/types.ts +29 -5
- package/extensions/bytefly/footer/unpriced.ts +42 -17
- package/extensions/bytefly/header/component.ts +4 -20
- package/extensions/bytefly/panel/commands.ts +28 -1
- package/extensions/bytefly/panel/filter.ts +68 -0
- package/extensions/bytefly/panel/format.ts +16 -15
- package/extensions/bytefly/panel/join.ts +56 -13
- package/extensions/bytefly/panel/prices.ts +43 -0
- package/extensions/bytefly/panel/settings.ts +72 -23
- package/extensions/bytefly/panel/tui/entries.ts +26 -8
- package/extensions/bytefly/panel/tui/frame.ts +130 -93
- package/extensions/bytefly/panel/tui/keys.ts +352 -0
- package/extensions/bytefly/panel/tui/list-view.ts +25 -46
- package/extensions/bytefly/panel/tui/panes.ts +76 -0
- package/extensions/bytefly/panel/tui/section-view.ts +126 -0
- package/extensions/bytefly/panel/tui/settings-section.ts +54 -125
- package/extensions/bytefly/panel/tui/settings-view.ts +30 -131
- package/extensions/bytefly/panel/tui/side-pane.ts +224 -0
- package/extensions/bytefly/panel/tui.ts +306 -259
- package/extensions/check/commands.ts +2 -1
- package/extensions/check/render.ts +10 -1
- package/extensions/file-browse/config/config.ts +4 -19
- package/extensions/file-browse/files/classify.ts +136 -0
- package/extensions/file-browse/files/files.ts +8 -547
- package/extensions/file-browse/files/index.ts +90 -0
- package/extensions/file-browse/files/list.ts +261 -0
- package/extensions/file-browse/files/tree.ts +101 -0
- package/extensions/file-browse/index.ts +27 -48
- package/extensions/file-browse/live/live.ts +1 -1
- package/extensions/file-browse/transport/router.ts +12 -6
- package/extensions/file-browse/transport/server.ts +61 -28
- package/extensions/file-browse/view/chrome.ts +4 -4
- package/extensions/file-browse/view/client.ts +3 -1
- package/extensions/file-browse/view/emit.ts +51 -0
- package/extensions/file-browse/view/html.ts +78 -46
- package/extensions/file-browse/view/live-client.ts +2 -2
- package/extensions/file-browse/view/markdown.ts +23 -26
- package/extensions/file-browse/view/palette.ts +2 -2
- package/extensions/file-browse/view/raw-html.ts +27 -22
- package/extensions/file-browse/view/tree.ts +1 -1
- package/extensions/file-browse/view/urls.ts +20 -6
- package/extensions/goal/tui.ts +12 -2
- package/extensions/job/runtime.ts +4 -4
- package/extensions/job/tools.ts +19 -7
- package/extensions/job/tui.ts +2 -28
- package/extensions/plan/commands.ts +2 -2
- package/extensions/plan/feedback.ts +88 -18
- package/extensions/plan/format.ts +10 -3
- package/extensions/plan/index.ts +24 -2
- package/extensions/plan/plan-files.ts +88 -0
- package/extensions/plan/plan-text.ts +114 -0
- package/extensions/plan/plans.ts +19 -23
- package/extensions/plan/policy.ts +10 -5
- package/extensions/plan/runtime.ts +43 -4
- package/extensions/plan/todos.ts +51 -3
- package/extensions/plan/tools/exit.ts +10 -1
- package/extensions/plan/tools/todo-seed.ts +4 -2
- package/extensions/plan/tools/write.ts +12 -1
- package/extensions/plan/tools.ts +2 -2
- package/extensions/plan/types.ts +15 -0
- package/extensions/plan/web-blocks.ts +160 -0
- package/extensions/plan/web-chrome.ts +7 -5
- package/extensions/plan/web-client.ts +271 -325
- package/extensions/plan/web-collab.ts +7 -7
- package/extensions/plan/web-comments.ts +57 -16
- package/extensions/plan/web-composer.ts +91 -0
- package/extensions/plan/web-draft.ts +40 -0
- package/extensions/plan/web-keys.ts +258 -0
- package/extensions/plan/web-markdown.ts +1 -1
- package/extensions/plan/web-outline.ts +212 -0
- package/extensions/plan/web-page.ts +59 -86
- package/extensions/plan/web-payload.ts +188 -0
- package/extensions/plan/web-plan.ts +160 -0
- package/extensions/plan/web-router.ts +0 -1
- package/extensions/plan/web-settle.ts +201 -0
- package/extensions/plan/web-share-session.ts +24 -55
- package/extensions/plan/web-share.ts +19 -37
- package/extensions/plan/web-write.ts +112 -0
- package/extensions/plan/web.ts +77 -130
- package/extensions/rewind/commands/review.ts +2 -1
- package/extensions/serve/config.ts +37 -0
- package/extensions/serve/index.ts +71 -0
- package/extensions/serve/registry.ts +251 -0
- package/extensions/session-bus/join-request.ts +16 -9
- package/extensions/session-bus/result.ts +1 -27
- package/extensions/session-bus/rows.ts +7 -3
- package/extensions/session-bus/runtime.ts +100 -38
- package/extensions/session-bus/store.ts +14 -2
- package/extensions/session-bus/tools.ts +5 -4
- package/extensions/session-bus/tui.ts +1 -1
- package/extensions/session-bus/types.ts +10 -1
- package/extensions/subagent/commands.ts +2 -1
- package/extensions/team/commands.ts +44 -9
- package/extensions/team/format.ts +14 -0
- package/extensions/team/index.ts +2 -1
- package/extensions/team/tools.ts +30 -9
- package/extensions/team/types.ts +17 -0
- package/extensions/todo/index.ts +4 -1
- package/extensions/todo/nudge.ts +8 -2
- package/extensions/todo/tui.ts +21 -3
- package/extensions/wechat/bridge.ts +43 -172
- package/extensions/wechat/intake.ts +245 -0
- package/extensions/worktree/commands.ts +24 -17
- package/extensions/worktree/exit.ts +12 -6
- package/extensions/worktree/git.ts +22 -14
- package/extensions/worktree/guard.ts +15 -285
- package/extensions/worktree/handoff.ts +10 -2
- package/extensions/worktree/index.ts +2 -1
- package/extensions/worktree/lifecycle.ts +2 -0
- package/extensions/worktree/registry.ts +20 -11
- package/extensions/worktree/root-tools.ts +13 -3
- package/extensions/worktree/runtime.ts +8 -2
- package/extensions/worktree/screen.ts +15 -8
- package/extensions/worktree/shell.ts +303 -0
- package/extensions/worktree/state.ts +1 -1
- package/extensions/worktree/status.ts +42 -9
- package/extensions/worktree/tools.ts +19 -13
- package/lib/README.md +9 -7
- package/lib/board/card.ts +192 -3
- package/lib/board/links.ts +26 -2
- package/lib/board/page-pointer.ts +111 -32
- package/lib/board/round-text.ts +11 -2
- package/lib/board/round.ts +25 -20
- package/lib/board/rules.ts +144 -11
- package/lib/board/session.ts +84 -18
- package/lib/board/store.ts +268 -55
- package/lib/board/view.ts +114 -24
- package/lib/bus-request.ts +83 -0
- package/lib/format.ts +22 -0
- package/lib/http/index.ts +11 -7
- package/lib/http/mount.ts +142 -0
- package/lib/http/port.ts +25 -0
- package/lib/http/serve.ts +170 -43
- package/lib/http/server.ts +3 -3
- package/lib/http/sink.ts +34 -9
- package/lib/plan-entry.ts +20 -13
- package/lib/policy.ts +14 -1
- package/lib/requirements.ts +2 -2
- package/lib/session-label.ts +23 -7
- package/lib/session-roster.ts +74 -3
- package/lib/session-tool.ts +37 -21
- package/lib/spawn.ts +37 -3
- package/lib/stray-fields.ts +45 -0
- package/lib/tool-names.ts +13 -0
- package/lib/tui/README.md +6 -3
- package/lib/tui/glyphs.ts +3 -6
- package/lib/tui/render-cache.ts +29 -0
- package/lib/tui/tool.ts +30 -1
- package/lib/web/README.md +52 -2
- package/lib/web/client-runtime.ts +149 -0
- package/lib/web/index.ts +4 -2
- package/lib/web/markup.ts +106 -2
- package/lib/web/names.ts +42 -0
- package/package.json +3 -2
- package/extensions/board/config.ts +0 -36
- package/extensions/board/view/actions.ts +0 -306
- package/extensions/board/writes.ts +0 -99
- package/extensions/bytefly/panel/tui/extensions-view.ts +0 -40
- package/extensions/bytefly/panel/tui/skills-view.ts +0 -50
- package/extensions/job/process.ts +0 -35
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The client's wiring: the tabs, the lane picker, the
|
|
3
|
-
* and the live frame stream.
|
|
2
|
+
* The client's wiring: the tabs, the lane picker, the filter, and the live frame stream.
|
|
4
3
|
*
|
|
5
4
|
* A frame is a whole snapshot, so every message repaints from it rather than patching
|
|
6
5
|
* it — the renderer decides what actually changed. The only client state is which tab
|
|
@@ -12,19 +11,13 @@ export const BOARD_LIVE_JS = String.raw`(function () {
|
|
|
12
11
|
"use strict";
|
|
13
12
|
var board = window.bfBoard || (window.bfBoard = {});
|
|
14
13
|
var token = new URLSearchParams(window.location.search).get("token") || "";
|
|
15
|
-
var LANE_LABEL = { todo: "To do", doing: "Doing", blocked: "Blocked", done: "Done" };
|
|
16
|
-
|
|
17
|
-
/** The lane's word, for the dialog's move buttons. */
|
|
18
|
-
board.laneLabel = function (lane) {
|
|
19
|
-
return LANE_LABEL[lane] || lane;
|
|
20
|
-
};
|
|
21
14
|
|
|
22
15
|
/** The badge's text and its kit status role: connecting, live, reconnecting. */
|
|
23
16
|
function badge(state, at) {
|
|
24
17
|
var node = document.getElementById("board-status");
|
|
25
18
|
if (!node) return;
|
|
26
19
|
node.setAttribute("data-state", state);
|
|
27
|
-
node.className = "bf-status
|
|
20
|
+
node.className = "bf-status " + N.status +
|
|
28
21
|
(state === "live" ? " bf-status--saved" : state === "reconnecting" ? " bf-status--error" : "");
|
|
29
22
|
// "now" would read as "live · now ago", so a fresh frame says only "live".
|
|
30
23
|
if (state === "live") {
|
|
@@ -87,104 +80,6 @@ export const BOARD_LIVE_JS = String.raw`(function () {
|
|
|
87
80
|
|
|
88
81
|
board.showView = showView;
|
|
89
82
|
|
|
90
|
-
/**
|
|
91
|
-
* The composer writes one card: a new one, or the one the card dialog was showing.
|
|
92
|
-
*
|
|
93
|
-
* The mode lives here rather than in the form, because the form is a draft: leaving
|
|
94
|
-
* edit mode puts the dialog back the way the toolbar's button opens it, and the text
|
|
95
|
-
* it held was the card's, not the reader's.
|
|
96
|
-
*/
|
|
97
|
-
var composer = { mode: "add", card: undefined };
|
|
98
|
-
|
|
99
|
-
/** The title's character count, shown from the first character typed. */
|
|
100
|
-
function showTitleCount() {
|
|
101
|
-
var field = document.getElementById("new-title");
|
|
102
|
-
var count = document.getElementById("new-title-count");
|
|
103
|
-
if (!field || !count) return;
|
|
104
|
-
var max = Number(field.getAttribute("maxlength"));
|
|
105
|
-
if (!max) return;
|
|
106
|
-
var used = field.value.length;
|
|
107
|
-
count.textContent = used + "/" + max;
|
|
108
|
-
count.hidden = used === 0;
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/** Dress the dialog for a new card or for the one being edited, and fill it in. */
|
|
112
|
-
function setComposerMode(mode, card) {
|
|
113
|
-
var heading = document.getElementById("board-new-title");
|
|
114
|
-
var submit = document.getElementById("new-submit");
|
|
115
|
-
var hint = document.getElementById("composer-hint");
|
|
116
|
-
var form = document.getElementById("board-composer");
|
|
117
|
-
var was = composer.mode;
|
|
118
|
-
composer.mode = mode;
|
|
119
|
-
composer.card = mode === "edit" ? card : undefined;
|
|
120
|
-
if (mode === "edit") {
|
|
121
|
-
var title = document.getElementById("new-title");
|
|
122
|
-
var paths = document.getElementById("new-paths");
|
|
123
|
-
var priority = document.getElementById("new-priority");
|
|
124
|
-
if (title) title.value = card.title;
|
|
125
|
-
if (paths) paths.value = (card.paths || []).join(" ");
|
|
126
|
-
if (priority) priority.value = board.priorityOf(card);
|
|
127
|
-
if (heading) heading.textContent = "Edit card #" + card.id;
|
|
128
|
-
if (submit) submit.textContent = "Save";
|
|
129
|
-
if (hint) hint.textContent = "links are recorded with the board tool, while the card is being planned";
|
|
130
|
-
showTitleCount();
|
|
131
|
-
return;
|
|
132
|
-
}
|
|
133
|
-
// Leaving edit mode throws its text away; a draft written here survives Esc.
|
|
134
|
-
if (was === "edit" && form) form.reset();
|
|
135
|
-
if (heading) heading.textContent = "New card";
|
|
136
|
-
if (submit) submit.textContent = "Add card";
|
|
137
|
-
if (hint && board.composerHint !== undefined) hint.textContent = board.composerHint;
|
|
138
|
-
showTitleCount();
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
/** Open whichever mode the composer is dressed for, with the caret in the title. */
|
|
142
|
-
function openComposerForm() {
|
|
143
|
-
if (!window.bfNew) return;
|
|
144
|
-
// The kit focuses a dialog's first control, which here is its close button; a card
|
|
145
|
-
// starts with a title, so the caret belongs in that field.
|
|
146
|
-
window.bfNew.open();
|
|
147
|
-
var input = document.getElementById("new-title");
|
|
148
|
-
if (input) input.focus();
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
/** Open the new-card dialog. */
|
|
152
|
-
board.openComposer = function () {
|
|
153
|
-
if (composer.mode === "edit") setComposerMode("add");
|
|
154
|
-
openComposerForm();
|
|
155
|
-
};
|
|
156
|
-
|
|
157
|
-
/** Open it on one card: the same two fields, prefilled, with Save in place of Add. */
|
|
158
|
-
board.editCard = function (card) {
|
|
159
|
-
if (!window.bfNew) return;
|
|
160
|
-
board.closeCard();
|
|
161
|
-
setComposerMode("edit", card);
|
|
162
|
-
openComposerForm();
|
|
163
|
-
var title = document.getElementById("new-title");
|
|
164
|
-
// The end of the title is where a typo is, so that is where the caret starts.
|
|
165
|
-
if (title && title.setSelectionRange) title.setSelectionRange(title.value.length, title.value.length);
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
/** Close it and keep the draft: only Cancel throws one away. */
|
|
169
|
-
board.closeComposer = function () {
|
|
170
|
-
if (window.bfNew) window.bfNew.close();
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
/** Discard the draft and close, which is what its own Cancel button does. */
|
|
174
|
-
board.cancelComposer = function () {
|
|
175
|
-
var form = document.getElementById("board-composer");
|
|
176
|
-
if (form) form.reset();
|
|
177
|
-
setComposerMode("add");
|
|
178
|
-
board.closeComposer();
|
|
179
|
-
};
|
|
180
|
-
|
|
181
|
-
board.composerOpen = function () {
|
|
182
|
-
return window.bfNew ? window.bfNew.isOpen() : false;
|
|
183
|
-
};
|
|
184
|
-
|
|
185
|
-
board.focusComposer = function () {
|
|
186
|
-
board.openComposer();
|
|
187
|
-
};
|
|
188
83
|
board.focusFilter = function () {
|
|
189
84
|
var input = document.getElementById("board-filter");
|
|
190
85
|
if (input) {
|
|
@@ -234,8 +129,8 @@ export const BOARD_LIVE_JS = String.raw`(function () {
|
|
|
234
129
|
if (!lanes || lanes.childElementCount > 0) return;
|
|
235
130
|
lanes.setAttribute("data-skeleton", "1");
|
|
236
131
|
for (var i = 0; i < 4; i++) {
|
|
237
|
-
var lane = board.el("section",
|
|
238
|
-
for (var j = 0; j < 2; j++) lane.appendChild(board.el("div",
|
|
132
|
+
var lane = board.el("section", N.lane);
|
|
133
|
+
for (var j = 0; j < 2; j++) lane.appendChild(board.el("div", N.skeleton));
|
|
239
134
|
lanes.appendChild(lane);
|
|
240
135
|
}
|
|
241
136
|
}
|
|
@@ -260,7 +155,7 @@ export const BOARD_LIVE_JS = String.raw`(function () {
|
|
|
260
155
|
|
|
261
156
|
/** The live channel: a reconnect is the browser's own retry, not a second request. */
|
|
262
157
|
function stream() {
|
|
263
|
-
var source = new EventSource("/events?token=" + encodeURIComponent(token));
|
|
158
|
+
var source = new EventSource(pageBase() + "/events?token=" + encodeURIComponent(token));
|
|
264
159
|
source.onopen = function () {
|
|
265
160
|
badge("live", board.lastFrame);
|
|
266
161
|
};
|
|
@@ -278,70 +173,7 @@ export const BOARD_LIVE_JS = String.raw`(function () {
|
|
|
278
173
|
};
|
|
279
174
|
}
|
|
280
175
|
|
|
281
|
-
/** The
|
|
282
|
-
function wireComposer() {
|
|
283
|
-
var form = document.getElementById("board-composer");
|
|
284
|
-
if (!form) return;
|
|
285
|
-
// The hint is the page's, not this module's: a second copy of the sentence would drift.
|
|
286
|
-
var hint = document.getElementById("composer-hint");
|
|
287
|
-
board.composerHint = hint ? hint.textContent : "";
|
|
288
|
-
// The field is capped at 120 characters, and a cap nobody can see reads as a bug the
|
|
289
|
-
// first time typing stops; the count appears with the first character.
|
|
290
|
-
var titleField = document.getElementById("new-title");
|
|
291
|
-
if (titleField) titleField.addEventListener("input", showTitleCount);
|
|
292
|
-
form.addEventListener("submit", function (event) {
|
|
293
|
-
event.preventDefault();
|
|
294
|
-
var title = document.getElementById("new-title");
|
|
295
|
-
if (!title.value.trim()) {
|
|
296
|
-
title.focus();
|
|
297
|
-
return;
|
|
298
|
-
}
|
|
299
|
-
var paths = document.getElementById("new-paths").value.split(/\s+/).filter(Boolean);
|
|
300
|
-
var priorityField = document.getElementById("new-priority");
|
|
301
|
-
var priority = priorityField ? priorityField.value : "normal";
|
|
302
|
-
if (composer.mode === "edit") {
|
|
303
|
-
var card = composer.card;
|
|
304
|
-
if (!card) return;
|
|
305
|
-
// Every field goes, including the unchanged ones: an edit means "this is what it
|
|
306
|
-
// should say", and a priority changed back to normal is a change too.
|
|
307
|
-
board.act("edit", card, { title: title.value.trim(), paths: paths, priority: priority }).then(function (payload) {
|
|
308
|
-
if (payload) board.cancelComposer();
|
|
309
|
-
});
|
|
310
|
-
return;
|
|
311
|
-
}
|
|
312
|
-
var input = { title: title.value.trim(), paths: paths };
|
|
313
|
-
if (priority !== "normal") input.priority = priority;
|
|
314
|
-
board.add(input).then(function (payload) {
|
|
315
|
-
if (!payload) return;
|
|
316
|
-
// The dialog stays open: a lead publishes several cards in a row.
|
|
317
|
-
form.reset();
|
|
318
|
-
showTitleCount();
|
|
319
|
-
title.focus();
|
|
320
|
-
});
|
|
321
|
-
});
|
|
322
|
-
var trigger = document.getElementById("board-new");
|
|
323
|
-
if (trigger) {
|
|
324
|
-
trigger.addEventListener("click", function () {
|
|
325
|
-
if (board.composerOpen()) board.closeComposer();
|
|
326
|
-
else board.openComposer();
|
|
327
|
-
});
|
|
328
|
-
}
|
|
329
|
-
// The dialog's single closer: Esc and the backdrop keep the draft, this does not.
|
|
330
|
-
var cancel = document.getElementById("board-cancel");
|
|
331
|
-
if (cancel) {
|
|
332
|
-
cancel.addEventListener("click", function () {
|
|
333
|
-
board.cancelComposer();
|
|
334
|
-
});
|
|
335
|
-
}
|
|
336
|
-
var first = document.getElementById("board-first-card");
|
|
337
|
-
if (first) {
|
|
338
|
-
first.addEventListener("click", function () {
|
|
339
|
-
board.openComposer();
|
|
340
|
-
});
|
|
341
|
-
}
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
/** The filter, the note button, and the two tab sets. */
|
|
176
|
+
/** The filter and the two tab sets. */
|
|
345
177
|
function wireChrome() {
|
|
346
178
|
var filter = document.getElementById("board-filter");
|
|
347
179
|
if (filter) {
|
|
@@ -349,9 +181,6 @@ export const BOARD_LIVE_JS = String.raw`(function () {
|
|
|
349
181
|
board.applyFilter();
|
|
350
182
|
});
|
|
351
183
|
}
|
|
352
|
-
var noteAdd = document.getElementById("card-dialog-note-add");
|
|
353
|
-
if (noteAdd) noteAdd.addEventListener("click", function () { board.addNote(); });
|
|
354
|
-
|
|
355
184
|
var tabs = document.querySelectorAll("[data-view][role='tab']");
|
|
356
185
|
for (var i = 0; i < tabs.length; i++) {
|
|
357
186
|
tabs[i].addEventListener("click", function (event) {
|
|
@@ -389,7 +218,6 @@ export const BOARD_LIVE_JS = String.raw`(function () {
|
|
|
389
218
|
board.collapsed = {};
|
|
390
219
|
showView(new URLSearchParams(window.location.search).get("view") === "team" ? "team" : "board");
|
|
391
220
|
wireChrome();
|
|
392
|
-
wireComposer();
|
|
393
221
|
skeletons();
|
|
394
222
|
board.bindKeys(document);
|
|
395
223
|
stream();
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The board page's class vocabulary, as one table.
|
|
3
|
+
*
|
|
4
|
+
* The document (`page.ts`) and the client bundle cannot import from each other, so a
|
|
5
|
+
* class name used to be a string in each — and two of them were assembled at runtime
|
|
6
|
+
* (`"board-card__reason" + kind`), which is why nothing could compare what the page
|
|
7
|
+
* wears with what `styles.ts` draws. Reading both halves from this table makes those two
|
|
8
|
+
* sets enumerable, and `pages.test.ts` holds them equal.
|
|
9
|
+
*
|
|
10
|
+
* A name here is a **class**, not an id: ids are how the client finds an element
|
|
11
|
+
* (`getElementById`), they are not styled, and `pages.test.ts` pins their list.
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
/** Every `board-*` class the page wears. Keys are what the code reads; values are what CSS matches. */
|
|
15
|
+
export const BOARD_CLASS = {
|
|
16
|
+
// The shell, the toolbar, and the two panel wrappers.
|
|
17
|
+
toolbar: "board-toolbar",
|
|
18
|
+
controls: "board-controls",
|
|
19
|
+
main: "board-main",
|
|
20
|
+
panel: "board-panel",
|
|
21
|
+
filter: "board-filter",
|
|
22
|
+
picker: "board-picker",
|
|
23
|
+
progress: "board-progress",
|
|
24
|
+
status: "board-status",
|
|
25
|
+
noCards: "board-no-cards",
|
|
26
|
+
skeleton: "board-skeleton",
|
|
27
|
+
|
|
28
|
+
// One lane: a tray of cards with a head that sticks and folds.
|
|
29
|
+
lanes: "board-lanes",
|
|
30
|
+
lane: "board-lane",
|
|
31
|
+
laneHead: "board-lane__head",
|
|
32
|
+
laneDot: "board-lane__dot",
|
|
33
|
+
laneToggle: "board-lane__toggle",
|
|
34
|
+
laneCount: "board-lane__count",
|
|
35
|
+
laneList: "board-lane__list",
|
|
36
|
+
laneEmpty: "board-lane__empty",
|
|
37
|
+
|
|
38
|
+
// One card, and the two tones its reason line can take.
|
|
39
|
+
card: "board-card",
|
|
40
|
+
cardHead: "board-card__head",
|
|
41
|
+
cardId: "board-card__id",
|
|
42
|
+
cardTitle: "board-card__title",
|
|
43
|
+
cardMeta: "board-card__meta",
|
|
44
|
+
cardPaths: "board-card__paths",
|
|
45
|
+
cardReason: "board-card__reason",
|
|
46
|
+
cardReasonBlocked: "board-card__reason--blocked",
|
|
47
|
+
cardReasonUnplanned: "board-card__reason--unplanned",
|
|
48
|
+
|
|
49
|
+
// The card dialog: the record of one card, as a ladder of facts and its notes.
|
|
50
|
+
dialogPanel: "board-dialog__panel",
|
|
51
|
+
dialogHead: "board-dialog__head",
|
|
52
|
+
dialogId: "board-dialog__id",
|
|
53
|
+
dialogState: "board-dialog__state",
|
|
54
|
+
dialogBody: "board-dialog__body",
|
|
55
|
+
dialogSection: "board-dialog__section",
|
|
56
|
+
dialogHeading: "board-dialog__heading",
|
|
57
|
+
dialogCount: "board-dialog__count",
|
|
58
|
+
dialogFacts: "board-dialog__facts",
|
|
59
|
+
dialogPaths: "board-dialog__paths",
|
|
60
|
+
dialogPath: "board-dialog__path",
|
|
61
|
+
dialogValue: "board-dialog__value",
|
|
62
|
+
dialogPlan: "board-dialog__plan",
|
|
63
|
+
dialogNotes: "board-dialog__notes",
|
|
64
|
+
dialogNote: "board-dialog__note",
|
|
65
|
+
dialogNoteFoot: "board-dialog__note-foot",
|
|
66
|
+
dialogNoteBy: "board-dialog__note-by",
|
|
67
|
+
dialogNoteAt: "board-dialog__note-at",
|
|
68
|
+
dialogNoteText: "board-dialog__note-text",
|
|
69
|
+
|
|
70
|
+
// The Team tab: the queue of decisions, the roster, and the Done strip.
|
|
71
|
+
team: "board-team",
|
|
72
|
+
teamGrid: "board-team__grid",
|
|
73
|
+
teamGroup: "board-team__group",
|
|
74
|
+
teamCaption: "board-team__caption",
|
|
75
|
+
teamQueue: "board-team__queue",
|
|
76
|
+
teamDecision: "board-team__decision",
|
|
77
|
+
teamOpen: "board-team__open",
|
|
78
|
+
teamRef: "board-team__ref",
|
|
79
|
+
teamTitle: "board-team__title",
|
|
80
|
+
teamFoot: "board-team__foot",
|
|
81
|
+
teamWhy: "board-team__why",
|
|
82
|
+
teamWhyBlocked: "board-team__why--blocked",
|
|
83
|
+
teamRoster: "board-team__roster",
|
|
84
|
+
teamMate: "board-team__mate",
|
|
85
|
+
teamDot: "board-team__dot",
|
|
86
|
+
teamName: "board-team__name",
|
|
87
|
+
teamMets: "board-team__mets",
|
|
88
|
+
teamEmpty: "board-team__empty",
|
|
89
|
+
teamDone: "board-team__done",
|
|
90
|
+
teamId: "board-team__id",
|
|
91
|
+
} as const satisfies Readonly<Record<string, string>>;
|
|
@@ -13,8 +13,17 @@
|
|
|
13
13
|
* contract test holds every class to the kit or to this page's stylesheet.
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
baseAttribute,
|
|
18
|
+
brandLockup,
|
|
19
|
+
button,
|
|
20
|
+
docHead,
|
|
21
|
+
keysSheet,
|
|
22
|
+
tabTitle,
|
|
23
|
+
themeToggle,
|
|
24
|
+
} from "../../../lib/web/index.ts";
|
|
17
25
|
import { BOARD_KEYS } from "./keys.ts";
|
|
26
|
+
import { BOARD_CLASS as N } from "./names.ts";
|
|
18
27
|
import { BOARD_CSS } from "./styles.ts";
|
|
19
28
|
|
|
20
29
|
/** The two views the page offers. */
|
|
@@ -23,135 +32,97 @@ export type PageView = "board" | "team";
|
|
|
23
32
|
/**
|
|
24
33
|
* Render the document for one initial view.
|
|
25
34
|
*
|
|
26
|
-
* Both panels are rendered and one is `hidden`, so switching tabs costs no request
|
|
27
|
-
*
|
|
28
|
-
* a
|
|
29
|
-
* page with no button the server would refuse.
|
|
35
|
+
* Both panels are rendered and one is `hidden`, so switching tabs costs no request and a
|
|
36
|
+
* shared link opens the tab it names. The document has no form and no write affordance:
|
|
37
|
+
* a page that cannot change a card cannot offer a button the server would refuse.
|
|
30
38
|
*/
|
|
31
|
-
export function renderBoardPage(input: { view: PageView;
|
|
39
|
+
export function renderBoardPage(input: { view: PageView; base?: string }): string {
|
|
32
40
|
const team = input.view === "team";
|
|
41
|
+
// The page answers under the session's mount, so its own script and theme are named
|
|
42
|
+
// from that prefix rather than from the origin's root.
|
|
43
|
+
const base = input.base ?? "";
|
|
33
44
|
return `<!doctype html>
|
|
34
|
-
<html lang="en" data-view="${team ? "team" : "board"}"
|
|
45
|
+
<html lang="en" data-view="${team ? "team" : "board"}"${baseAttribute(base)}>
|
|
35
46
|
<head>
|
|
36
|
-
${docHead({ title: "board", css: BOARD_CSS })}
|
|
47
|
+
${docHead({ title: tabTitle("board"), css: BOARD_CSS, base })}
|
|
37
48
|
</head>
|
|
38
49
|
<body class="bf-app">
|
|
39
50
|
<h1 class="sr-only">board — the shared feature list for this repository</h1>
|
|
40
51
|
<a class="skip" href="#main">Skip to the cards</a>
|
|
41
|
-
<header class="bf-app__header"><div class="bf-toolbar
|
|
52
|
+
<header class="bf-app__header"><div class="bf-toolbar ${N.toolbar}">
|
|
42
53
|
${brandLockup("board")}
|
|
43
|
-
<span class="bf-chip bf-chip--accent" id="board-me" title="
|
|
44
|
-
<p class="bf-status
|
|
54
|
+
<span class="bf-chip bf-chip--accent" id="board-me" title="This page is served as this session, and it cannot change a card">board</span>
|
|
55
|
+
<p class="bf-status ${N.status}" id="board-status" data-state="connecting" aria-live="polite">connecting…</p>
|
|
45
56
|
<span class="bf-spacer"></span>
|
|
46
|
-
<div class="
|
|
57
|
+
<div class="${N.controls}">
|
|
47
58
|
<div class="bf-segmented" role="tablist" aria-label="View">
|
|
48
59
|
<button type="button" class="bf-segmented__item" role="tab" id="tab-board" data-view="board" aria-controls="panel-board"${team ? "" : ' aria-current="page"'}>Board</button>
|
|
49
60
|
<button type="button" class="bf-segmented__item" role="tab" id="tab-team" data-view="team" aria-controls="panel-team"${team ? ' aria-current="page"' : ""}>Team</button>
|
|
50
61
|
</div>
|
|
51
|
-
<p class="bf-progress
|
|
62
|
+
<p class="bf-progress ${N.progress}" id="board-progress" hidden>
|
|
52
63
|
<span class="bf-progress__track"><span class="bf-progress__fill" id="board-progress-fill"></span></span>
|
|
53
64
|
<span id="board-progress-text">0/0</span>
|
|
54
65
|
</p>
|
|
55
|
-
<label class="bf-field
|
|
66
|
+
<label class="bf-field ${N.filter}"><span class="sr-only">Filter cards</span>
|
|
56
67
|
<input class="bf-input" id="board-filter" type="search" placeholder="filter cards" autocomplete="off">
|
|
57
68
|
</label>
|
|
58
|
-
${button({ label: "+ New card", id: "board-new", title: "New card — n", attrs: { "aria-haspopup": "dialog" } })}
|
|
59
69
|
${button({ label: "?", variant: "icon", id: "keys-open", title: "Keyboard shortcuts", attrs: { "aria-haspopup": "dialog" } })}
|
|
60
70
|
${themeToggle()}
|
|
61
71
|
</div>
|
|
62
72
|
</div></header>
|
|
63
|
-
<main class="bf-app__main
|
|
64
|
-
<
|
|
65
|
-
|
|
66
|
-
<div class="
|
|
67
|
-
<div class="
|
|
68
|
-
<div class="bf-empty board-no-cards" id="board-no-cards" hidden>
|
|
73
|
+
<main class="bf-app__main ${N.main}" id="main" tabindex="-1">
|
|
74
|
+
<section class="${N.panel}" id="panel-board" role="tabpanel" aria-labelledby="tab-board"${team ? " hidden" : ""}>
|
|
75
|
+
<div class="bf-segmented ${N.picker}" id="board-picker" role="tablist" aria-label="Lane"></div>
|
|
76
|
+
<div class="${N.lanes}" id="board-lanes"></div>
|
|
77
|
+
<div class="bf-empty ${N.noCards}" id="board-no-cards" hidden>
|
|
69
78
|
<p class="bf-empty__title">No cards yet</p>
|
|
70
79
|
<p class="bf-empty__sub">One card per feature: what it delivers, who holds it, and where to start.</p>
|
|
71
|
-
<
|
|
80
|
+
<p class="bf-empty__sub"><code>board add</code> makes one — this page reads, and the tools write.</p>
|
|
72
81
|
</div>
|
|
73
82
|
</section>
|
|
74
|
-
<section class="
|
|
75
|
-
<div class="
|
|
83
|
+
<section class="${N.panel}" id="panel-team" role="tabpanel" aria-labelledby="tab-team"${team ? "" : " hidden"}>
|
|
84
|
+
<div class="${N.team}" id="board-team"></div>
|
|
76
85
|
</section>
|
|
77
86
|
</main>
|
|
78
87
|
<footer class="bf-app__footer"><span id="board-where"></span></footer>
|
|
79
88
|
|
|
80
|
-
<div class="bf-dialog" id="board-new-dialog" hidden>
|
|
81
|
-
<div class="bf-dialog__panel" role="dialog" aria-modal="true" aria-labelledby="board-new-title">
|
|
82
|
-
<div class="bf-dialog__head">
|
|
83
|
-
<h2 id="board-new-title">New card</h2>
|
|
84
|
-
</div>
|
|
85
|
-
<form class="board-composer" id="board-composer" autocomplete="off">
|
|
86
|
-
<div class="bf-dialog__body">
|
|
87
|
-
<div class="board-composer__fields">
|
|
88
|
-
<label class="board-composer__field board-composer__field--title">
|
|
89
|
-
<span class="board-composer__label">Title<span class="board-composer__count" id="new-title-count" hidden></span></span>
|
|
90
|
-
<span class="bf-field"><input class="bf-input" id="new-title" name="title" placeholder="what it delivers, in one line" maxlength="120" required></span>
|
|
91
|
-
</label>
|
|
92
|
-
<label class="board-composer__field">
|
|
93
|
-
<span class="board-composer__label">Paths</span>
|
|
94
|
-
<span class="bf-field"><input class="bf-input" id="new-paths" name="paths" placeholder="extensions/board lib/web" aria-describedby="composer-hint"></span>
|
|
95
|
-
</label>
|
|
96
|
-
<label class="board-composer__field">
|
|
97
|
-
<span class="board-composer__label">Priority</span>
|
|
98
|
-
<span class="bf-field"><select class="bf-input board-composer__select" id="new-priority" name="priority">
|
|
99
|
-
<option value="high">high</option>
|
|
100
|
-
<option value="normal" selected>normal</option>
|
|
101
|
-
<option value="low">low</option>
|
|
102
|
-
</select></span>
|
|
103
|
-
</label>
|
|
104
|
-
</div>
|
|
105
|
-
<p class="bf-dialog__hint" id="composer-hint">one card per feature — its steps live in the owner's plan and todo</p>
|
|
106
|
-
</div>
|
|
107
|
-
<div class="bf-dialog__actions">
|
|
108
|
-
<button class="bf-btn" type="button" id="board-cancel">Cancel</button>
|
|
109
|
-
<button class="bf-btn bf-btn--primary" type="submit" id="new-submit">Add card</button>
|
|
110
|
-
</div>
|
|
111
|
-
</form>
|
|
112
|
-
</div>
|
|
113
|
-
</div>
|
|
114
|
-
|
|
115
89
|
<div class="bf-dialog" id="board-card-dialog" hidden>
|
|
116
|
-
<div class="bf-dialog__panel" role="dialog" aria-modal="true" aria-labelledby="card-dialog-title" tabindex="-1">
|
|
117
|
-
<div class="bf-dialog__head">
|
|
90
|
+
<div class="bf-dialog__panel ${N.dialogPanel}" role="dialog" aria-modal="true" aria-labelledby="card-dialog-title" tabindex="-1">
|
|
91
|
+
<div class="bf-dialog__head ${N.dialogHead}">
|
|
92
|
+
<span class="${N.dialogId}" id="card-dialog-id"></span>
|
|
118
93
|
<h2 id="card-dialog-title"></h2>
|
|
119
|
-
<span class="board-dialog__state" id="card-dialog-state"></span>
|
|
120
94
|
<span class="bf-spacer"></span>
|
|
95
|
+
<span class="${N.dialogState}" id="card-dialog-state"></span>
|
|
121
96
|
<button type="button" class="bf-btn bf-btn--icon" id="card-dialog-close" aria-label="Close">×</button>
|
|
122
97
|
</div>
|
|
123
|
-
<div class="bf-dialog__body">
|
|
124
|
-
<p class="
|
|
125
|
-
<
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
<
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
<
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
<div class="bf-dialog__body"><p class="board-confirm__body" id="confirm-body"></p></div>
|
|
146
|
-
<div class="bf-dialog__actions">
|
|
147
|
-
<button type="button" class="bf-btn" id="confirm-cancel">Cancel</button>
|
|
148
|
-
<button type="button" class="bf-btn bf-btn--primary" id="confirm-ok">Confirm</button>
|
|
98
|
+
<div class="bf-dialog__body ${N.dialogBody}">
|
|
99
|
+
<p class="sr-only" id="card-dialog-where" aria-live="polite"></p>
|
|
100
|
+
<section class="${N.dialogSection}" aria-labelledby="card-dialog-card-head">
|
|
101
|
+
<h3 class="${N.dialogHeading}" id="card-dialog-card-head">the card</h3>
|
|
102
|
+
<dl class="${N.dialogFacts}">
|
|
103
|
+
<dt id="card-dialog-owner-label">owner</dt><dd class="${N.dialogValue}" id="card-dialog-owner"></dd>
|
|
104
|
+
<dt id="card-dialog-priority-label" hidden>priority</dt><dd class="${N.dialogValue}" id="card-dialog-priority" hidden></dd>
|
|
105
|
+
<dt id="card-dialog-updated-label">updated</dt><dd class="${N.dialogValue}" id="card-dialog-updated"></dd>
|
|
106
|
+
<dt id="card-dialog-lane-label" hidden>in lane</dt><dd class="${N.dialogValue}" id="card-dialog-lane" hidden></dd>
|
|
107
|
+
<dt id="card-dialog-by-label">last edited</dt><dd class="${N.dialogValue}" id="card-dialog-by"></dd>
|
|
108
|
+
<dt id="card-dialog-landed-label" hidden>landed</dt><dd class="${N.dialogValue}" id="card-dialog-landed" hidden></dd>
|
|
109
|
+
<dt id="card-dialog-depends-label" hidden>waits on</dt><dd class="${N.dialogValue}" id="card-dialog-depends" hidden></dd>
|
|
110
|
+
<dt id="card-dialog-dependents-label" hidden>waited on by</dt><dd class="${N.dialogValue}" id="card-dialog-dependents" hidden></dd>
|
|
111
|
+
<dt id="card-dialog-plan-label" hidden>plan</dt><dd class="${N.dialogValue} ${N.dialogPlan}" id="card-dialog-plan" hidden></dd>
|
|
112
|
+
<dt id="card-dialog-paths-label" hidden>paths</dt><dd id="card-dialog-paths" hidden><ul class="${N.dialogPaths}" id="card-dialog-path-list"></ul></dd>
|
|
113
|
+
</dl>
|
|
114
|
+
</section>
|
|
115
|
+
<section class="${N.dialogSection}" aria-labelledby="card-dialog-notes-head">
|
|
116
|
+
<h3 class="${N.dialogHeading}" id="card-dialog-notes-head">notes<span class="${N.dialogCount}" id="card-dialog-note-count"></span></h3>
|
|
117
|
+
<ol class="${N.dialogNotes}" id="card-dialog-notes"></ol>
|
|
118
|
+
<p class="bf-dialog__hint" id="card-dialog-earlier" hidden></p>
|
|
119
|
+
</section>
|
|
149
120
|
</div>
|
|
150
121
|
</div>
|
|
151
122
|
</div>
|
|
152
123
|
|
|
153
124
|
${keysSheet(BOARD_KEYS)}
|
|
154
|
-
<script src="/app.js" defer></script>
|
|
125
|
+
<script src="${base}/app.js" defer></script>
|
|
155
126
|
</body>
|
|
156
127
|
</html>
|
|
157
128
|
`;
|