@gavin-hu/my-pi-agent 0.20.0 → 0.22.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 +46 -30
- package/README.zh-CN.md +40 -25
- package/assets/screens/board.png +0 -0
- package/assets/screens/chrome.png +0 -0
- package/assets/screens/file-browse.png +0 -0
- package/assets/screens/plan-review.png +0 -0
- package/assets/screens/plan.png +0 -0
- package/docs/extensions/README.md +22 -23
- package/docs/extensions/board.md +397 -168
- package/docs/extensions/bytefly.md +234 -197
- package/docs/extensions/check.md +15 -17
- package/docs/extensions/context.md +35 -15
- package/docs/extensions/file-browse.md +57 -63
- package/docs/extensions/file-open.md +9 -11
- package/docs/extensions/file-read.md +12 -14
- package/docs/extensions/goal.md +15 -17
- package/docs/extensions/job.md +54 -42
- package/docs/extensions/memory.md +17 -19
- package/docs/extensions/plan.md +197 -140
- package/docs/extensions/question.md +16 -18
- package/docs/extensions/rewind.md +17 -19
- package/docs/extensions/serve.md +145 -0
- package/docs/extensions/session-bus.md +38 -16
- package/docs/extensions/subagent.md +23 -25
- package/docs/extensions/team.md +83 -44
- package/docs/extensions/todo.md +18 -18
- package/docs/extensions/watch.md +31 -31
- package/docs/extensions/web-access.md +2 -4
- package/docs/extensions/wechat.md +34 -35
- package/docs/extensions/worktree.md +56 -50
- package/extensions/board/commands.ts +79 -25
- package/extensions/board/format.ts +43 -24
- package/extensions/board/index.ts +17 -4
- package/extensions/board/input.ts +4 -9
- package/extensions/board/join.ts +5 -20
- package/extensions/board/lazy-page.ts +40 -0
- package/extensions/board/page.ts +65 -93
- package/extensions/board/result.ts +30 -39
- package/extensions/board/rows.ts +62 -25
- package/extensions/board/schema.ts +49 -11
- package/extensions/board/snapshot.ts +8 -6
- package/extensions/board/tone.ts +22 -0
- package/extensions/board/tools.ts +154 -55
- package/extensions/board/transport/router.ts +3 -5
- package/extensions/board/transport/server.ts +59 -28
- package/extensions/board/types.ts +36 -42
- package/extensions/board/view/card.ts +229 -0
- package/extensions/board/view/client.ts +35 -16
- package/extensions/board/view/dialog.ts +169 -6
- package/extensions/board/view/empty.ts +47 -0
- package/extensions/board/view/keys.ts +13 -2
- package/extensions/board/view/live.ts +90 -28
- package/extensions/board/view/names.ts +13 -4
- package/extensions/board/view/page.ts +38 -13
- package/extensions/board/view/render.ts +219 -277
- package/extensions/board/view/styles.ts +130 -32
- package/extensions/board/view/team.ts +297 -76
- package/extensions/bytefly/block/fallback.ts +1 -1
- package/extensions/bytefly/doctor/runtime.ts +5 -2
- package/extensions/bytefly/footer/collect/label.ts +60 -0
- package/extensions/bytefly/footer/collect/snapshot.ts +8 -1
- package/extensions/bytefly/footer/config.ts +7 -8
- package/extensions/bytefly/footer/register.ts +33 -4
- package/extensions/bytefly/footer/render/footer.ts +18 -4
- 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 +10 -7
- package/extensions/bytefly/footer/render/segments.ts +41 -38
- package/extensions/bytefly/footer/render/status.ts +0 -11
- package/extensions/bytefly/footer/types.ts +7 -0
- package/extensions/bytefly/header/component.ts +4 -20
- package/extensions/bytefly/header/config.ts +1 -1
- package/extensions/bytefly/index.ts +32 -6
- package/extensions/bytefly/mode/editor.ts +115 -0
- package/extensions/bytefly/panel/commands.ts +210 -120
- package/extensions/bytefly/panel/format.ts +4 -4
- package/extensions/bytefly/panel/settings.ts +73 -6
- package/extensions/bytefly/panel/tui/keys.ts +2 -6
- package/extensions/bytefly/update/cache.ts +1 -1
- package/extensions/bytefly/update/register.ts +4 -4
- package/extensions/bytefly/working/config.ts +5 -7
- package/extensions/bytefly/working/format.ts +10 -13
- package/extensions/bytefly/working/register.ts +22 -27
- package/extensions/check/commands.ts +3 -2
- package/extensions/check/render.ts +10 -1
- package/extensions/check/{tui.ts → view/check-screens.ts} +14 -19
- package/extensions/context/commands.ts +2 -2
- package/extensions/context/{tui.ts → view/context-report.ts} +51 -19
- package/extensions/file-browse/config/config.ts +4 -19
- package/extensions/file-browse/index.ts +89 -79
- package/extensions/file-browse/live/live.ts +1 -1
- package/extensions/file-browse/transport/router.ts +12 -4
- 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/html.ts +73 -40
- package/extensions/file-browse/view/live-client.ts +2 -2
- package/extensions/file-browse/view/markdown.ts +13 -14
- package/extensions/file-browse/view/palette.ts +2 -2
- package/extensions/file-browse/view/raw-html.ts +9 -9
- package/extensions/file-browse/view/tree.ts +1 -1
- package/extensions/file-browse/view/urls.ts +20 -6
- package/extensions/file-read/render.ts +4 -4
- package/extensions/goal/plan-route.ts +2 -2
- package/extensions/goal/tui.ts +12 -2
- package/extensions/job/child.ts +527 -0
- package/extensions/job/clock.ts +142 -0
- package/extensions/job/commands.ts +4 -2
- package/extensions/job/config.ts +8 -1
- package/extensions/job/format.ts +22 -1
- package/extensions/job/log-writer.ts +13 -2
- package/extensions/job/registry.ts +63 -7
- package/extensions/job/render.ts +34 -0
- package/extensions/job/runtime.ts +296 -655
- package/extensions/job/session.ts +132 -19
- package/extensions/job/store.ts +7 -7
- package/extensions/job/tools.ts +21 -9
- package/extensions/job/types.ts +47 -0
- package/extensions/job/{tui.ts → view/jobs-screen.ts} +19 -99
- package/extensions/job/waiters.ts +44 -1
- package/extensions/memory/commands.ts +4 -2
- package/extensions/memory/{tui.ts → view/memory-picker.ts} +12 -12
- package/extensions/plan/commands.ts +12 -9
- package/extensions/plan/feedback.ts +62 -16
- package/extensions/plan/format.ts +8 -2
- package/extensions/plan/git-bash.ts +115 -16
- package/extensions/plan/index.ts +58 -51
- package/extensions/plan/plan-index.ts +4 -2
- package/extensions/plan/plan-text.ts +114 -0
- package/extensions/plan/plans.ts +5 -54
- package/extensions/plan/policy.ts +8 -10
- package/extensions/plan/review-lazy.ts +85 -0
- package/extensions/plan/review.ts +2 -2
- package/extensions/plan/runtime.ts +61 -41
- package/extensions/plan/sections.ts +1 -1
- package/extensions/plan/steps.ts +3 -3
- package/extensions/plan/todos.ts +1 -1
- package/extensions/plan/tools/cancel.ts +27 -62
- package/extensions/plan/tools/enter.ts +55 -81
- package/extensions/plan/tools/exit.ts +221 -238
- package/extensions/plan/tools/refusal.ts +18 -0
- package/extensions/plan/tools/steps.ts +75 -92
- package/extensions/plan/tools/write.ts +77 -120
- package/extensions/plan/tools.ts +188 -30
- package/extensions/plan/tui.ts +5 -5
- package/extensions/plan/types.ts +48 -5
- package/extensions/plan/web-blocks.ts +76 -5
- package/extensions/plan/web-chrome.ts +4 -0
- package/extensions/plan/web-client.ts +102 -2
- package/extensions/plan/web-collab.ts +4 -4
- package/extensions/plan/web-comments.ts +39 -8
- package/extensions/plan/web-composer.ts +14 -3
- package/extensions/plan/web-keys.ts +149 -25
- package/extensions/plan/web-lazy.ts +53 -0
- package/extensions/plan/web-markdown.ts +10 -68
- package/extensions/plan/web-outline.ts +22 -2
- package/extensions/plan/web-page.ts +56 -86
- package/extensions/plan/web-payload.ts +188 -0
- package/extensions/plan/web-plan.ts +23 -13
- package/extensions/plan/web-router.ts +18 -9
- package/extensions/plan/web-settle.ts +14 -7
- 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 +67 -131
- package/extensions/question/tools.ts +6 -2
- package/extensions/question/{tui.ts → view/question-screen.ts} +11 -11
- package/extensions/rewind/commands/commands.ts +6 -3
- package/extensions/rewind/state/runtime.ts +1 -1
- package/extensions/rewind/{view/status.ts → status.ts} +4 -4
- package/extensions/rewind/view/review-tui.ts +4 -4
- package/extensions/serve/config.ts +39 -0
- package/extensions/serve/index.ts +85 -0
- package/extensions/serve/registry.ts +260 -0
- package/extensions/session-bus/commands.ts +11 -6
- package/extensions/session-bus/join-request.ts +17 -9
- package/extensions/session-bus/mail-watch.ts +26 -17
- package/extensions/session-bus/protocol.ts +15 -3
- package/extensions/session-bus/resolve.ts +5 -7
- package/extensions/session-bus/result.ts +1 -27
- package/extensions/session-bus/runtime.ts +40 -12
- package/extensions/session-bus/store.ts +101 -19
- package/extensions/session-bus/tools.ts +5 -4
- package/extensions/session-bus/types.ts +19 -0
- package/extensions/session-bus/{tui.ts → view/session-list.ts} +16 -16
- package/extensions/subagent/agents.ts +1 -1
- package/extensions/subagent/commands.ts +4 -2
- package/extensions/subagent/format.ts +15 -15
- package/extensions/subagent/index.ts +1 -1
- package/extensions/subagent/render.ts +3 -3
- package/extensions/subagent/{background-tui.ts → view/agents-board.ts} +11 -11
- package/extensions/team/commands.ts +24 -5
- package/extensions/team/dispatch.ts +18 -4
- package/extensions/team/format.ts +17 -2
- package/extensions/team/index.ts +9 -8
- package/extensions/team/schema.ts +6 -5
- package/extensions/team/tools.ts +107 -45
- package/extensions/team/types.ts +3 -2
- package/extensions/todo/commands.ts +4 -2
- package/extensions/todo/format.ts +11 -0
- package/extensions/todo/render.ts +57 -0
- package/extensions/todo/runtime.ts +1 -1
- package/extensions/todo/tools.ts +1 -1
- package/extensions/todo/{tui.ts → view/todo-screen.ts} +18 -90
- package/extensions/todo/widget.ts +47 -0
- package/extensions/watch/commands.ts +4 -2
- package/extensions/watch/format.ts +36 -7
- package/extensions/watch/render.ts +57 -0
- package/extensions/watch/runtime.ts +27 -22
- package/extensions/watch/tools.ts +4 -4
- package/extensions/watch/{tui.ts → view/watch-screen.ts} +18 -80
- package/extensions/web-access/fetch/tool.ts +12 -4
- package/extensions/wechat/commands/commands.ts +18 -14
- package/extensions/wechat/index.ts +72 -37
- package/extensions/wechat/state/credentials.ts +1 -1
- package/extensions/wechat/state/state.ts +1 -1
- package/extensions/wechat/text/status.ts +112 -0
- package/extensions/wechat/types.ts +2 -2
- package/extensions/wechat/view/status-screen.ts +4 -103
- package/extensions/worktree/commands.ts +11 -9
- package/extensions/worktree/enter.ts +2 -2
- package/extensions/worktree/exit.ts +2 -2
- package/extensions/worktree/index.ts +5 -5
- package/extensions/worktree/screen.ts +3 -3
- package/extensions/worktree/status.ts +2 -2
- package/extensions/worktree/tools.ts +113 -116
- package/lib/README.md +28 -13
- package/lib/board/actions.ts +111 -0
- package/lib/board/artifact.ts +149 -0
- package/lib/board/card.ts +163 -52
- package/lib/board/config.ts +58 -0
- package/lib/board/contract.ts +113 -0
- package/lib/board/join.ts +38 -0
- package/lib/board/lanes.ts +182 -0
- package/lib/board/lead.ts +130 -0
- package/lib/board/limits.ts +78 -0
- package/lib/board/links.ts +31 -10
- package/lib/board/page-pointer.ts +30 -5
- package/lib/board/read.ts +83 -0
- package/lib/board/reason.ts +102 -0
- package/lib/board/round-text.ts +68 -6
- package/lib/board/round.ts +31 -15
- package/lib/board/rules.ts +151 -307
- package/lib/board/seat.ts +35 -0
- package/lib/board/session.ts +199 -51
- package/lib/board/skipped.ts +22 -0
- package/lib/board/steps.ts +487 -0
- package/lib/board/store.ts +119 -36
- package/lib/board/tone.ts +92 -0
- package/lib/board/view.ts +81 -63
- package/lib/bus-request.ts +83 -0
- package/{extensions/watch → lib}/child.ts +40 -21
- package/lib/env.ts +65 -7
- package/lib/format.ts +13 -0
- package/lib/http/index.ts +17 -11
- package/lib/http/mount.ts +142 -0
- package/lib/http/port.ts +30 -0
- package/lib/http/serve.ts +170 -46
- package/lib/http/server.ts +3 -3
- package/lib/lazy.ts +45 -0
- package/lib/plan-entry.ts +46 -21
- package/lib/plans/paths.ts +82 -0
- package/lib/policy.ts +9 -6
- package/lib/report.ts +3 -2
- package/lib/requirements.ts +10 -10
- package/lib/session-label.ts +129 -7
- package/lib/session-roster.ts +43 -1
- package/lib/session-tool.ts +39 -22
- package/lib/stray-fields.ts +7 -2
- package/lib/tool-names.ts +5 -10
- package/lib/tui/README.md +29 -11
- package/lib/tui/chrome.ts +16 -15
- package/lib/tui/cursor.ts +18 -3
- package/lib/tui/glyphs.ts +11 -11
- package/lib/tui/render-cache.ts +29 -0
- package/lib/tui/tokens.ts +33 -2
- package/lib/tui/tool.ts +33 -4
- package/lib/web/README.md +21 -0
- package/lib/web/client-runtime.ts +27 -1
- package/lib/web/components.ts +5 -1
- package/lib/web/index.ts +1 -0
- package/lib/web/markdown.ts +83 -0
- package/lib/web/markup.ts +20 -2
- package/lib/web/runtime.ts +9 -10
- package/package.json +8 -2
- package/skills/init/SKILL.md +5 -5
- package/skills/next/SKILL.md +3 -5
- package/extensions/board/config.ts +0 -36
- package/extensions/board/seat.ts +0 -28
- package/lib/tui/index.ts +0 -34
- package/skills/project-docs/SKILL.md +0 -133
- package/skills/project-docs/references/audit.md +0 -131
- package/skills/project-docs/references/changelog.md +0 -105
- package/skills/project-docs/references/discovery.md +0 -127
- package/skills/project-docs/references/readme.md +0 -80
- package/skills/project-docs/references/release.md +0 -84
- package/skills/project-docs/references/roadmap.md +0 -93
- package/skills/project-docs/scripts/state.ts +0 -316
|
@@ -1,37 +1,41 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* The board page's
|
|
3
|
-
* attached tab, and the idle grace that
|
|
2
|
+
* The board page's mount: the prefix it holds on the session's one server, the snapshot
|
|
3
|
+
* pushed to every attached tab, and the idle grace that gives the prefix back when the
|
|
4
|
+
* last one leaves.
|
|
4
5
|
*
|
|
5
|
-
* `lib/http` owns the socket
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
* stops the ticker.
|
|
6
|
+
* `lib/http` owns the socket and `serve` owns the session's port, so this module owns only
|
|
7
|
+
* the board's own decisions: where it answers (`BOARD_BASE`), when a frame is pushed (only
|
|
8
|
+
* when a card's revision moved), who receives it, and what stops the ticker.
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
|
+
import { BOARD_BASE } from "../../../lib/board/page-pointer.ts";
|
|
12
|
+
import type { BoardView } from "../../../lib/board/view.ts";
|
|
11
13
|
import {
|
|
12
14
|
type EventSink,
|
|
15
|
+
type HttpMount,
|
|
13
16
|
LOOPBACK_HOSTS,
|
|
14
|
-
type
|
|
17
|
+
type MountSource,
|
|
18
|
+
type MountSpec,
|
|
15
19
|
mintToken,
|
|
16
|
-
serveLoopback,
|
|
17
20
|
tokenMatches,
|
|
18
21
|
} from "../../../lib/http/index.ts";
|
|
19
22
|
import type { IdleReason } from "../../../lib/http/sink.ts";
|
|
20
23
|
import { defaultInterval, type RepeatTimer } from "../../../lib/timer.ts";
|
|
21
|
-
import type { BoardView } from "../../../lib/board/view.ts";
|
|
22
24
|
import type { PageView } from "../view/page.ts";
|
|
23
25
|
import { type BoardRouterContext, handleBoardRequest } from "./router.ts";
|
|
24
26
|
|
|
25
27
|
/** How often the page is offered a frame; a frame is sent only when the signature moved. */
|
|
26
|
-
|
|
28
|
+
const PUSH_MS = 1_000;
|
|
27
29
|
|
|
28
30
|
/** Largest request body the page accepts: a card and a note are small. */
|
|
29
|
-
|
|
31
|
+
const MAX_BODY_BYTES = 64 * 1_024;
|
|
30
32
|
|
|
31
33
|
/** What the page is built from. The lifecycle (`page.ts`) supplies all of it. */
|
|
32
34
|
export interface BoardServerOptions {
|
|
33
|
-
/**
|
|
34
|
-
|
|
35
|
+
/** Where the prefix comes from: the session's server, or a suite's own. */
|
|
36
|
+
mount: MountSource;
|
|
37
|
+
/** The Pi session asking, so the prefix lands on this session's socket. */
|
|
38
|
+
session: string;
|
|
35
39
|
/** The page's token; minted here when the caller has none, so the note carries one. */
|
|
36
40
|
token?: string;
|
|
37
41
|
page: (view: PageView) => Promise<string>;
|
|
@@ -41,8 +45,17 @@ export interface BoardServerOptions {
|
|
|
41
45
|
onIdle?: (reason: IdleReason) => void;
|
|
42
46
|
/** Grace after the last stream leaves; 0 or absent waits for `close()`. */
|
|
43
47
|
idleMs?: number;
|
|
44
|
-
/** Grace for a page no tab ever attached to; absent leaves the
|
|
48
|
+
/** Grace for a page no tab ever attached to; absent leaves the mount open. */
|
|
45
49
|
firstIdleMs?: number;
|
|
50
|
+
/**
|
|
51
|
+
* A read that failed while building a frame.
|
|
52
|
+
*
|
|
53
|
+
* A snapshot can fail for reasons the page does not control — the board directory removed
|
|
54
|
+
* or made unreadable under a running page — and a ticker nobody awaits must not turn that
|
|
55
|
+
* into an unhandled rejection in the session. The frame is dropped, the next tick tries
|
|
56
|
+
* again, and this is the one place the failure is said. Absent, the page stays quiet.
|
|
57
|
+
*/
|
|
58
|
+
onError?: (error: unknown) => void;
|
|
46
59
|
/** The interval seam, so a test drives the ticker instead of waiting. */
|
|
47
60
|
interval?: RepeatTimer;
|
|
48
61
|
}
|
|
@@ -59,10 +72,15 @@ export interface BoardServer {
|
|
|
59
72
|
close: () => Promise<void>;
|
|
60
73
|
}
|
|
61
74
|
|
|
62
|
-
|
|
75
|
+
/** The page, or the reason the session's server has no prefix for it. */
|
|
76
|
+
export type BoardServerResult = { kind: "served"; server: BoardServer } | { kind: "refused"; reason: string };
|
|
77
|
+
|
|
78
|
+
export async function createBoardServer(options: BoardServerOptions): Promise<BoardServerResult> {
|
|
63
79
|
const token = options.token ?? mintToken();
|
|
64
80
|
const clients = new Set<EventSink>();
|
|
65
81
|
const interval = options.interval ?? defaultInterval;
|
|
82
|
+
/** A frame that could not be built is not sent; the next tick tries again. */
|
|
83
|
+
const report = (error: unknown): void => options.onError?.(error);
|
|
66
84
|
let pushed = "";
|
|
67
85
|
|
|
68
86
|
const ctx: BoardRouterContext = {
|
|
@@ -84,8 +102,10 @@ export async function createBoardServer(options: BoardServerOptions): Promise<Bo
|
|
|
84
102
|
for (const sink of clients) sink.write(frame);
|
|
85
103
|
};
|
|
86
104
|
|
|
87
|
-
|
|
88
|
-
|
|
105
|
+
/** The prefix this page asks for: the session's server holds it, not this module. */
|
|
106
|
+
const spec: Omit<MountSpec, "handler"> = {
|
|
107
|
+
session: options.session,
|
|
108
|
+
prefix: BOARD_BASE,
|
|
89
109
|
maxBodyBytes: MAX_BODY_BYTES,
|
|
90
110
|
...(options.idleMs === undefined || options.onIdle === undefined
|
|
91
111
|
? {}
|
|
@@ -96,6 +116,9 @@ export async function createBoardServer(options: BoardServerOptions): Promise<Bo
|
|
|
96
116
|
onIdle: options.onIdle,
|
|
97
117
|
},
|
|
98
118
|
}),
|
|
119
|
+
};
|
|
120
|
+
const outcome = await options.mount({
|
|
121
|
+
...spec,
|
|
99
122
|
async handler(request) {
|
|
100
123
|
const result = await handleBoardRequest(ctx, request.method, request.url, request.headers);
|
|
101
124
|
if (!result.stream) return { result };
|
|
@@ -105,10 +128,13 @@ export async function createBoardServer(options: BoardServerOptions): Promise<Bo
|
|
|
105
128
|
clients.add(sink);
|
|
106
129
|
// A tab that just attached gets the current frame without waiting for a
|
|
107
130
|
// card to change — otherwise a quiet board looks like a dead page.
|
|
108
|
-
void options
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
131
|
+
void options
|
|
132
|
+
.view()
|
|
133
|
+
.then((view) => {
|
|
134
|
+
pushed = view.sig;
|
|
135
|
+
sink.write(`data: ${JSON.stringify(view)}\n\n`);
|
|
136
|
+
})
|
|
137
|
+
.catch(report);
|
|
112
138
|
return () => {
|
|
113
139
|
clients.delete(sink);
|
|
114
140
|
};
|
|
@@ -116,8 +142,10 @@ export async function createBoardServer(options: BoardServerOptions): Promise<Bo
|
|
|
116
142
|
};
|
|
117
143
|
},
|
|
118
144
|
});
|
|
145
|
+
if (outcome.kind === "refused") return { kind: "refused", reason: outcome.reason };
|
|
146
|
+
const mount: HttpMount = outcome.mount;
|
|
119
147
|
|
|
120
|
-
const stopTicker = interval(PUSH_MS, () => void push());
|
|
148
|
+
const stopTicker = interval(PUSH_MS, () => void push().catch(report));
|
|
121
149
|
let closed = false;
|
|
122
150
|
const close = async (): Promise<void> => {
|
|
123
151
|
if (closed) return;
|
|
@@ -125,14 +153,17 @@ export async function createBoardServer(options: BoardServerOptions): Promise<Bo
|
|
|
125
153
|
stopTicker();
|
|
126
154
|
for (const sink of clients) sink.end();
|
|
127
155
|
clients.clear();
|
|
128
|
-
await
|
|
156
|
+
await mount.release();
|
|
129
157
|
};
|
|
130
158
|
|
|
131
159
|
return {
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
160
|
+
kind: "served",
|
|
161
|
+
server: {
|
|
162
|
+
port: mount.port,
|
|
163
|
+
url: `${mount.origin}${mount.base}/?token=${token}`,
|
|
164
|
+
watchers: () => clients.size,
|
|
165
|
+
pushed: () => pushed,
|
|
166
|
+
close,
|
|
167
|
+
},
|
|
137
168
|
};
|
|
138
169
|
}
|
|
@@ -10,53 +10,47 @@
|
|
|
10
10
|
import type { Card } from "../../lib/board/card.ts";
|
|
11
11
|
import type { Actor } from "../../lib/board/rules.ts";
|
|
12
12
|
import type { RoundSummary } from "../../lib/board/round.ts";
|
|
13
|
+
import { BOARD_ACTIONS, type BoardAction } from "../../lib/board/actions.ts";
|
|
13
14
|
|
|
14
15
|
/** The tool's name. One extension registers it, so it stays here. */
|
|
15
16
|
export const BOARD_TOOL = "board";
|
|
16
17
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"release",
|
|
23
|
-
"reclaim",
|
|
24
|
-
"move",
|
|
25
|
-
"note",
|
|
26
|
-
"plan",
|
|
27
|
-
"edit",
|
|
28
|
-
"archive",
|
|
29
|
-
"drop",
|
|
30
|
-
] as const;
|
|
31
|
-
|
|
32
|
-
/** One action. */
|
|
33
|
-
export type BoardAction = (typeof BOARD_ACTIONS)[number];
|
|
18
|
+
// The actions are the board's, not the tool's: plan mode's read-only policy and the todo
|
|
19
|
+
// nudge classify a call by the same table, and nothing under `lib/` may import this
|
|
20
|
+
// file. Re-exported here so the schema, the description, and the suites keep one import
|
|
21
|
+
// for the tool's own vocabulary.
|
|
22
|
+
export { BOARD_ACTIONS, type BoardAction };
|
|
34
23
|
|
|
35
24
|
/**
|
|
36
|
-
* What each
|
|
25
|
+
* What each action does, in one sentence.
|
|
37
26
|
*
|
|
38
27
|
* The tool description is built from this table and the parameter's `action`
|
|
39
|
-
* description points at that sentence, so the
|
|
28
|
+
* description points at that sentence, so the actions are stated once. A new action is
|
|
40
29
|
* a row here and an entry in {@link BOARD_ACTIONS}; `test` holds the two together.
|
|
41
30
|
*/
|
|
42
31
|
export const BOARD_ACTION_DOCS: Record<BoardAction, string> = {
|
|
43
32
|
list:
|
|
44
|
-
"shows the board — `
|
|
33
|
+
"shows the board — `lane` filters one lane, `mine` only what this session holds, " +
|
|
45
34
|
"`query` matches a title, a holder, a path hint, the plan, or a note's text, `card` reads that one card " +
|
|
46
|
-
"instead of the board, and `round` adds the lead's rollup of owners, unassigned, unplanned,
|
|
47
|
-
"idle, which always covers the whole board — and a listing that names no lane leaves out the `done` cards, " +
|
|
35
|
+
"instead of the board, which refuses the fields that narrow a listing beside it, and `round` adds the lead's rollup of owners, unassigned, unplanned, awaiting approval, " +
|
|
36
|
+
"blocked, idle, and done, which always covers the whole board — and a listing that names no lane leaves out the `done` cards, " +
|
|
48
37
|
"saying how many it left out",
|
|
49
|
-
add: "creates a `
|
|
50
|
-
claim: "
|
|
51
|
-
release: "gives it back",
|
|
52
|
-
reclaim: "takes it from whoever holds it",
|
|
53
|
-
move: "changes lane",
|
|
38
|
+
add: "creates a `backlog` card",
|
|
39
|
+
claim: "pulls one into `plan` for this session, when `plan` has room",
|
|
40
|
+
release: "gives it back to `backlog`",
|
|
41
|
+
reclaim: "takes it from whoever holds it, back to `backlog`",
|
|
42
|
+
move: "changes lane, through the one transition the board allows from where the card is; a card enters `execute` or `done` only while the plan it recorded is still in the store",
|
|
54
43
|
note: "appends to its history",
|
|
55
|
-
|
|
44
|
+
contract:
|
|
45
|
+
"records the plan file and the three lines a card promises — `goal`, `acceptance`, `scopeOut` — and drops any approval, because the wording changed; `path` is required and must be the plan `plan` action `write` saved in this repository's plan store",
|
|
46
|
+
approve:
|
|
47
|
+
"approves the contract: the lead (`board.lead`, `lead` by default, the name the page serves as), who does not hold the card, says the wording is right, which is what `plan` to `execute` requires, and `text` records what the signer saw",
|
|
48
|
+
review:
|
|
49
|
+
"records a spot-check of the finished work: the lead, who does not hold the card, in `execute`, which is what `done` requires, and `text` records what the signer saw",
|
|
56
50
|
edit: "fixes what the card says — title, path hints, priority, and what it waits for",
|
|
57
51
|
archive:
|
|
58
52
|
"sets a finished card aside, out of the board and still readable, or every finished card when no `card` is named",
|
|
59
|
-
drop: "deletes a `
|
|
53
|
+
drop: "deletes a `backlog` or a `done` card, or one you hold",
|
|
60
54
|
};
|
|
61
55
|
|
|
62
56
|
/**
|
|
@@ -71,34 +65,34 @@ export const BOARD_ACTION_DOCS: Record<BoardAction, string> = {
|
|
|
71
65
|
* a new field cannot be published without an action that acts on it.
|
|
72
66
|
*/
|
|
73
67
|
export const ACTION_FIELDS: Record<BoardAction, readonly string[]> = {
|
|
74
|
-
list: ["card", "
|
|
68
|
+
list: ["card", "lane", "mine", "query", "round", "archived"],
|
|
75
69
|
add: ["title", "paths", "priority", "note"],
|
|
76
70
|
claim: ["card"],
|
|
77
71
|
release: ["card"],
|
|
78
72
|
reclaim: ["card"],
|
|
79
|
-
move: ["card", "
|
|
73
|
+
move: ["card", "lane", "blockedBy", "blocker"],
|
|
80
74
|
note: ["card", "text"],
|
|
81
|
-
|
|
75
|
+
contract: ["card", "path", "goal", "acceptance", "scopeOut"],
|
|
76
|
+
approve: ["card", "text"],
|
|
77
|
+
review: ["card", "text"],
|
|
82
78
|
edit: ["card", "title", "paths", "priority", "dependsOn"],
|
|
83
79
|
archive: ["card"],
|
|
84
80
|
drop: ["card"],
|
|
85
81
|
};
|
|
86
82
|
|
|
87
|
-
/**
|
|
88
|
-
* The actions that change the board: everything but a read.
|
|
89
|
-
*
|
|
90
|
-
* One definition, read by the tool (does this call need a teammate?) and by plan
|
|
91
|
-
* mode (may this call run while planning?), so "a read" cannot mean two things.
|
|
92
|
-
*/
|
|
93
|
-
export const BOARD_MUTATING_ACTIONS: ReadonlySet<BoardAction> = new Set(
|
|
94
|
-
BOARD_ACTIONS.filter((action) => action !== "list"),
|
|
95
|
-
);
|
|
96
|
-
|
|
97
83
|
/** A result the transcript renders and the page's routes mirror. */
|
|
98
84
|
export interface BoardDetails {
|
|
99
85
|
action: BoardAction;
|
|
100
86
|
/** The serving session: the id Pi knows, and the label a card records. */
|
|
101
87
|
me: Actor;
|
|
88
|
+
/**
|
|
89
|
+
* Whether the read was narrowed to this session's own cards.
|
|
90
|
+
*
|
|
91
|
+
* Kept because the transcript says so: a listing that answers "what am I holding" draws
|
|
92
|
+
* those rows in the accent, and a listing that answers "what is on the board" does not
|
|
93
|
+
* pretend every row is the reader's.
|
|
94
|
+
*/
|
|
95
|
+
mine?: boolean;
|
|
102
96
|
/** The board a `list` read, in lane order. */
|
|
103
97
|
cards?: Card[];
|
|
104
98
|
/** The card an action produced, or the one a refusal left untouched. */
|
|
@@ -0,0 +1,229 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One card's face: the title, the facts a reader acts on, and the two marks that say
|
|
3
|
+
* where they have been.
|
|
4
|
+
*
|
|
5
|
+
* It is a fragment of the client bundle like `team.ts` and `dialog.ts`, and it reads the
|
|
6
|
+
* helpers the renderer publishes — the element builder, the chip, the age, the count's
|
|
7
|
+
* wording, and the reason rule spliced in from `lib/board/reason.ts` — so no rule about a
|
|
8
|
+
* card is stated twice.
|
|
9
|
+
*
|
|
10
|
+
* Two of the face's facts are longer than it can draw: the hints past its third path, the
|
|
11
|
+
* reason sentence, and the cards it is holding up. Each is on a hover title for the
|
|
12
|
+
* pointer, and each is named by the title button's `aria-describedby` for the keyboard,
|
|
13
|
+
* because `j`/`k` walks cards by focusing that button and never hovers anything.
|
|
14
|
+
*
|
|
15
|
+
* Every value arrives as `textContent`, never as markup: a title is model-written and a
|
|
16
|
+
* note is anything a person typed.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
export const BOARD_CARD_JS = String.raw`(function () {
|
|
20
|
+
"use strict";
|
|
21
|
+
var board = window.bfBoard || (window.bfBoard = {});
|
|
22
|
+
var el = board.el;
|
|
23
|
+
var chip = board.chip;
|
|
24
|
+
var ago = board.ago;
|
|
25
|
+
var plural = board.plural;
|
|
26
|
+
var reasonFor = board.reasonFor;
|
|
27
|
+
var reasonTone = board.reasonTone;
|
|
28
|
+
|
|
29
|
+
/** Path chips a lane card shows before it counts the rest. */
|
|
30
|
+
var PATH_CHIPS = 3;
|
|
31
|
+
/**
|
|
32
|
+
* The class that draws each tone, in the page kit's own words.
|
|
33
|
+
*
|
|
34
|
+
* A tone is a colour and the class is named after it, so the reason line for a missing
|
|
35
|
+
* contract, an approval nobody gave, and a spot-check nobody did are three kinds drawn in
|
|
36
|
+
* one tone — instead of three kinds sharing a class named after the first of them.
|
|
37
|
+
*/
|
|
38
|
+
var TONE_CLASS = {
|
|
39
|
+
muted: N.cardReasonMuted,
|
|
40
|
+
dim: N.cardReasonDim,
|
|
41
|
+
warn: N.cardReasonWarn,
|
|
42
|
+
accent: N.cardReasonAccent,
|
|
43
|
+
danger: N.cardReasonDanger,
|
|
44
|
+
success: N.cardReasonSuccess,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/** "3h", "just now" — the age a stamp is drawn with, read against the reader's clock. */
|
|
48
|
+
function sentence(wording, age) {
|
|
49
|
+
return wording.replace("{age}", age === "now" ? "just now" : age + " ago");
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* An age the ticker can keep honest: the stamp it was painted from, and the sentence
|
|
54
|
+
* saying which fact it is, which the ticker fills from the same stamp a second later.
|
|
55
|
+
*
|
|
56
|
+
* The wording is the stamp's rather than the ticker's because a card now carries two
|
|
57
|
+
* ages — when it was written to, and when its last note was filed — and a ticker that
|
|
58
|
+
* assumed "updated" would relabel one of them within a minute.
|
|
59
|
+
*/
|
|
60
|
+
function stamp(at, wording) {
|
|
61
|
+
var node = el("span", "", ago(at));
|
|
62
|
+
node.setAttribute("data-at", String(at));
|
|
63
|
+
node.setAttribute("data-at-title", wording);
|
|
64
|
+
node.title = sentence(wording, ago(at));
|
|
65
|
+
return node;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/** The one line under a card's meta: why it sits where it does. */
|
|
69
|
+
function reasonOf(card) {
|
|
70
|
+
// An order is the strongest reason a card is not being worked on, and the one a
|
|
71
|
+
// reader cannot see from the lane. The sentence is the snapshot's, so this line and
|
|
72
|
+
// the tool's refusal name one list.
|
|
73
|
+
if (card.waits !== undefined) return { text: "waits on " + card.waits, kind: "waiting" };
|
|
74
|
+
return reasonFor(card, ago);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* A path hint, or the +N chip that points at the rest.
|
|
79
|
+
*
|
|
80
|
+
* The hints past the third are the ones a reader needs before opening anything, so they
|
|
81
|
+
* are not dropped: the chip carries them on its hover one per line, and the list holds
|
|
82
|
+
* them again as text a screen reader reads, which is the whole of what the chip counts.
|
|
83
|
+
*/
|
|
84
|
+
function paths(card, limit) {
|
|
85
|
+
var list = el("ul", N.cardPaths);
|
|
86
|
+
list.id = "card-paths-" + card.id;
|
|
87
|
+
var shown = card.paths.slice(0, limit);
|
|
88
|
+
for (var i = 0; i < shown.length; i++) {
|
|
89
|
+
var item = el("li", "");
|
|
90
|
+
var hint = chip(shown[i]);
|
|
91
|
+
// A path longer than the card elides, and the whole of it is on the hover title: the
|
|
92
|
+
// card is often the only place its hints are read before opening the record.
|
|
93
|
+
hint.title = shown[i];
|
|
94
|
+
item.appendChild(hint);
|
|
95
|
+
list.appendChild(item);
|
|
96
|
+
}
|
|
97
|
+
if (card.paths.length > limit) {
|
|
98
|
+
var beyond = card.paths.slice(limit);
|
|
99
|
+
var rest = el("li", "");
|
|
100
|
+
var more = chip("+" + beyond.length);
|
|
101
|
+
more.title = beyond.join("\n");
|
|
102
|
+
// The count is a shape, not a word: what it counts is in the list below it, and a
|
|
103
|
+
// reader is owed the hints rather than "+3".
|
|
104
|
+
more.setAttribute("aria-hidden", "true");
|
|
105
|
+
rest.appendChild(more);
|
|
106
|
+
list.appendChild(rest);
|
|
107
|
+
for (var j = 0; j < beyond.length; j++) list.appendChild(el("li", "sr-only", beyond[j]));
|
|
108
|
+
}
|
|
109
|
+
return list;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/** Whether the reader is dragging a selection, which a click at the end of it would clear. */
|
|
113
|
+
function textSelected() {
|
|
114
|
+
var selection = window.getSelection ? window.getSelection() : null;
|
|
115
|
+
return selection !== null && String(selection) !== "";
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/** One card in its lane. */
|
|
119
|
+
function cardNode(card) {
|
|
120
|
+
var item = el("li", N.card);
|
|
121
|
+
item.setAttribute("data-card", card.id);
|
|
122
|
+
item.setAttribute("data-lane", card.lane);
|
|
123
|
+
item.setAttribute("data-rev", String(card.rev));
|
|
124
|
+
|
|
125
|
+
var head = el("p", N.cardHead);
|
|
126
|
+
head.appendChild(el("span", N.cardId, "#" + card.id));
|
|
127
|
+
// The title is the page's only focusable thing inside a card, so Enter and the focus
|
|
128
|
+
// ring have one home; the card around it is the larger target for a pointer. A native
|
|
129
|
+
// button's Enter and Space arrive as a click that bubbles to the card, so the card is
|
|
130
|
+
// the one handler and the button needs none.
|
|
131
|
+
var title = el("button", N.cardTitle, card.title);
|
|
132
|
+
title.type = "button";
|
|
133
|
+
head.appendChild(title);
|
|
134
|
+
item.appendChild(head);
|
|
135
|
+
item.addEventListener("click", function () {
|
|
136
|
+
// A drag that ends on a card is a reader selecting a title to copy, not opening it.
|
|
137
|
+
if (textSelected()) return;
|
|
138
|
+
board.openCard(card.id);
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
/** The face's clipped text, by id: what the title button describes. */
|
|
142
|
+
var described = [];
|
|
143
|
+
|
|
144
|
+
var meta = el("p", N.cardMeta);
|
|
145
|
+
meta.appendChild(card.owner ? chip(card.owner.label, "accent") : el("span", "", "unassigned"));
|
|
146
|
+
// The order inside a lane is the priority, so an urgent card says so: an order
|
|
147
|
+
// nobody can see reads as an accident. The lane's colour stays the card's own.
|
|
148
|
+
if (card.priority !== "normal") meta.appendChild(chip(card.priority, "outline"));
|
|
149
|
+
// The stamp, so the clock can keep the age honest between frames: a board can be
|
|
150
|
+
// quiet for ten minutes while the page still says "live".
|
|
151
|
+
meta.appendChild(stamp(card.updatedAt, "updated {age}"));
|
|
152
|
+
// A card with ten notes and a card with none drew the same face, and the notes are the
|
|
153
|
+
// card's history: the count says how much is there. The last note's own age earns a place
|
|
154
|
+
// only when it reads differently from the stamp beside it — a note is itself a write, so
|
|
155
|
+
// usually the two are one moment and the meta line printed the same bare number twice.
|
|
156
|
+
if (card.notes.length > 0) {
|
|
157
|
+
var last = card.notes[card.notes.length - 1];
|
|
158
|
+
var notes = el("span", N.cardNotes, plural(card.notes.length, "note"));
|
|
159
|
+
if (ago(last.at) !== ago(card.updatedAt)) {
|
|
160
|
+
notes.appendChild(document.createTextNode(" \u00b7 "));
|
|
161
|
+
notes.appendChild(stamp(last.at, "last note {age} by " + last.by));
|
|
162
|
+
}
|
|
163
|
+
meta.appendChild(notes);
|
|
164
|
+
}
|
|
165
|
+
item.appendChild(meta);
|
|
166
|
+
|
|
167
|
+
if (card.paths.length > 0) {
|
|
168
|
+
item.appendChild(paths(card, PATH_CHIPS));
|
|
169
|
+
// Only a truncated list is described: the hints a reader can already see are read in
|
|
170
|
+
// order with the card, and the ones behind the chip have nowhere else to be read.
|
|
171
|
+
if (card.paths.length > PATH_CHIPS) described.push("card-paths-" + card.id);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
var reason = reasonOf(card);
|
|
175
|
+
if (reason) {
|
|
176
|
+
// The tone is a lookup, not a suffix: a name assembled from a prefix is a name no
|
|
177
|
+
// test can compare with the stylesheet.
|
|
178
|
+
var line = el("p", N.cardReason + " " + TONE_CLASS[reasonTone(reason.kind)], reason.text);
|
|
179
|
+
line.id = "card-reason-" + card.id;
|
|
180
|
+
// It clamps on the card, so the full sentence is the tooltip.
|
|
181
|
+
line.title = reason.text;
|
|
182
|
+
item.appendChild(line);
|
|
183
|
+
described.push(line.id);
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// The other side of a link, on the face: what leaving this card undone holds up. It was
|
|
187
|
+
// dialog-only, so a card three others were waiting on looked like any other card.
|
|
188
|
+
if (card.dependents !== undefined) {
|
|
189
|
+
var holds = el("p", N.cardHolds, "holds up " + card.dependents);
|
|
190
|
+
holds.id = "card-holds-" + card.id;
|
|
191
|
+
holds.title = "holds up " + card.dependents;
|
|
192
|
+
item.appendChild(holds);
|
|
193
|
+
described.push(holds.id);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// The reason and the hints are drawn below the title, and a keyboard reader never
|
|
197
|
+
// hovers: describing the button with them is what makes a clamped line readable from
|
|
198
|
+
// j/k and from a screen reader alike.
|
|
199
|
+
if (described.length > 0) title.setAttribute("aria-describedby", described.join(" "));
|
|
200
|
+
|
|
201
|
+
return item;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
board.cardNode = cardNode;
|
|
205
|
+
board.sentence = sentence;
|
|
206
|
+
|
|
207
|
+
/** Set a mark on a card, or drop it, so one that lost it does not keep it. */
|
|
208
|
+
function marked(node, name, on) {
|
|
209
|
+
if (on) node.setAttribute(name, "true");
|
|
210
|
+
else node.removeAttribute(name);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Paint the card the reader is on and the cards they have already read.
|
|
215
|
+
*
|
|
216
|
+
* j/k walks the drawn cards and wraps, so walking a lane and coming back looked
|
|
217
|
+
* exactly like arriving for the first time. The two ids are the client's own — never
|
|
218
|
+
* sent anywhere — and this runs after every frame as well as on a focus and an open,
|
|
219
|
+
* because a lane is rebuilt whole when its cards move.
|
|
220
|
+
*/
|
|
221
|
+
board.markCards = function () {
|
|
222
|
+
var nodes = document.querySelectorAll(".board-card");
|
|
223
|
+
for (var i = 0; i < nodes.length; i++) {
|
|
224
|
+
var id = nodes[i].getAttribute("data-card");
|
|
225
|
+
marked(nodes[i], "data-current", board.current !== undefined && String(board.current) === id);
|
|
226
|
+
marked(nodes[i], "data-visited", board.visited !== undefined && board.visited[id] === true);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
})();`;
|
|
@@ -6,36 +6,55 @@
|
|
|
6
6
|
* Served as one string, because a page that opens six requests before it can paint
|
|
7
7
|
* is a page that looks broken for a moment. Nothing here is typed — no compiler sees
|
|
8
8
|
* a template string — so the split is by job and the class-contract test reads the
|
|
9
|
-
* result: `render.ts` paints, `
|
|
10
|
-
* `keys.ts` binds, and `live.ts` owns the
|
|
9
|
+
* result: `render.ts` paints a whole frame, `card.ts` draws one card's face, `team.ts`
|
|
10
|
+
* draws the round, `dialog.ts` opens one card, `keys.ts` binds, and `live.ts` owns the
|
|
11
|
+
* tabs and the stream.
|
|
11
12
|
*
|
|
12
|
-
* Load order is a dependency: `team`
|
|
13
|
-
* last, after every `board.*` handler it wires exists.
|
|
13
|
+
* Load order is a dependency: `card` and `team` take helpers from `render`, and `live`
|
|
14
|
+
* starts last, after every `board.*` handler it wires exists.
|
|
14
15
|
*/
|
|
15
16
|
|
|
16
17
|
import { MATCHES_QUERY_JS } from "../../../lib/board/card.ts";
|
|
17
|
-
import {
|
|
18
|
-
import {
|
|
18
|
+
import { REASON_FOR_JS } from "../../../lib/board/reason.ts";
|
|
19
|
+
import { DECISION_RANK_JS, DECISION_TONE_JS, REASON_TONE_JS } from "../../../lib/board/tone.ts";
|
|
20
|
+
import { LANES } from "../../../lib/board/lanes.ts";
|
|
21
|
+
import {
|
|
22
|
+
CLIENT_BASE_JS,
|
|
23
|
+
CLIENT_RUNTIME_JS,
|
|
24
|
+
dialogScript,
|
|
25
|
+
INLINE_MARKDOWN_JS,
|
|
26
|
+
keysDialogScript,
|
|
27
|
+
namesScript,
|
|
28
|
+
TAB_TITLE_JS,
|
|
29
|
+
THEME_TOGGLE_JS,
|
|
30
|
+
} from "../../../lib/web/index.ts";
|
|
31
|
+
import { PLURAL_JS, HISTORY_LINE_JS, HISTORY_OWNER_JS } from "../../../lib/board/round-text.ts";
|
|
32
|
+
import { skippedText } from "../../../lib/board/skipped.ts";
|
|
19
33
|
import { FORMAT_AGE_JS } from "../../../lib/format.ts";
|
|
20
|
-
import {
|
|
34
|
+
import { BOARD_CARD_JS } from "./card.ts";
|
|
21
35
|
import { BOARD_CLASS } from "./names.ts";
|
|
22
|
-
import {
|
|
36
|
+
import { BOARD_EMPTY_JS } from "./empty.ts";
|
|
37
|
+
import { dialogJs } from "./dialog.ts";
|
|
23
38
|
import { BOARD_KEYS_JS } from "./keys.ts";
|
|
24
39
|
import { BOARD_LIVE_JS } from "./live.ts";
|
|
25
40
|
import { renderJs } from "./render.ts";
|
|
26
|
-
import {
|
|
41
|
+
import { teamJs } from "./team.ts";
|
|
27
42
|
|
|
28
43
|
/**
|
|
29
44
|
* The whole client, in load order: paint, round, the card dialog, keys, wiring, start.
|
|
30
45
|
*
|
|
31
|
-
*
|
|
32
|
-
* shape, what a `query` matches,
|
|
33
|
-
*
|
|
46
|
+
* Seven rules cross the boundary as source — the count's wording, the tab title's
|
|
47
|
+
* shape, what a `query` matches, why a card sits where it does, how an age reads, which
|
|
48
|
+
* owners a round may state once, and how that one line reads: the browser has no import
|
|
49
|
+
* graph, and a second copy of any of them is exactly what this bundle is not allowed to hold.
|
|
34
50
|
*/
|
|
35
|
-
export const BOARD_CLIENT_JS = `${
|
|
36
|
-
${
|
|
37
|
-
${
|
|
38
|
-
${
|
|
51
|
+
export const BOARD_CLIENT_JS = `${CLIENT_BASE_JS}
|
|
52
|
+
${namesScript(BOARD_CLASS)}
|
|
53
|
+
${renderJs({ plural: PLURAL_JS, tabTitle: TAB_TITLE_JS, matches: MATCHES_QUERY_JS, reason: REASON_FOR_JS, age: FORMAT_AGE_JS, lanes: String(LANES.length), skipped: String(skippedText), decisions: DECISION_RANK_JS, decisionTones: DECISION_TONE_JS, reasonTones: REASON_TONE_JS, runtime: CLIENT_RUNTIME_JS })}
|
|
54
|
+
${teamJs({ historyOwner: HISTORY_OWNER_JS, historyLine: HISTORY_LINE_JS })}
|
|
55
|
+
${BOARD_CARD_JS}
|
|
56
|
+
${BOARD_EMPTY_JS}
|
|
57
|
+
${dialogJs({ inlineMarkdown: INLINE_MARKDOWN_JS })}
|
|
39
58
|
${BOARD_KEYS_JS}
|
|
40
59
|
${BOARD_LIVE_JS}
|
|
41
60
|
${THEME_TOGGLE_JS}
|