pi-roundtable-web 0.26.0 → 0.26.2
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/CHANGELOG.md +50 -0
- package/README.md +32 -12
- package/dist/chunk-account-nhpd9j90.js +1 -0
- package/dist/chunk-action-menu-ec9jqtxq.js +1 -0
- package/dist/chunk-agent-d1xfrawq.js +1 -0
- package/dist/chunk-arranged-rooms-bga8yk2v.js +1 -0
- package/dist/chunk-character-creator-k67f77w5.js +1 -0
- package/dist/chunk-codeeditor-jm2sm333.js +2 -0
- package/dist/chunk-confirm-wc5xraev.js +1 -0
- package/dist/chunk-connectors-mhnw6d79.js +1 -0
- package/dist/chunk-conversations-yjyq4g1y.js +1 -0
- package/dist/{chunk-drag-v41gx2mc.js → chunk-drag-nyq52ck7.js} +1 -1
- package/dist/chunk-entry-red1597w.js +1 -0
- package/dist/chunk-folder-list-x56y26yq.js +1 -0
- package/dist/chunk-folds-3p7s2nvx.js +1 -0
- package/dist/{chunk-gate-8k61stsc.js → chunk-gate-h41bv4y9.js} +1 -1
- package/dist/chunk-group-scene-7eb6jyev.js +1 -0
- package/dist/chunk-group-table-rca48kt0.js +1 -0
- package/dist/chunk-host-icons-x318gjqp.js +1 -0
- package/dist/chunk-host-picture-xswcakgy.js +1 -0
- package/dist/chunk-host-state-bpahvk54.js +1 -0
- package/dist/chunk-host-ui-tsbwwk38.js +1 -0
- package/dist/{chunk-index-hby0ap91.js → chunk-index-029209sy.js} +2 -2
- package/dist/chunk-index-066vzexw.js +2 -0
- package/dist/chunk-index-0h4h76kw.js +2 -0
- package/dist/chunk-index-0n6ja7my.js +1 -0
- package/dist/chunk-index-0q15ysrb.js +1 -0
- package/dist/chunk-index-0xj30mzw.js +13 -0
- package/dist/{chunk-index-rhtvcbsq.js → chunk-index-1f0j335v.js} +2 -2
- package/dist/chunk-index-2f5nfekb.js +8 -0
- package/dist/chunk-index-31ays46f.js +2 -0
- package/dist/chunk-index-43k1rjmx.js +2 -0
- package/dist/chunk-index-4gk88a0x.js +4 -0
- package/dist/chunk-index-4jf4jvfd.css +1 -0
- package/dist/chunk-index-4mf29sbx.js +2 -0
- package/dist/chunk-index-51n20yhb.js +2 -0
- package/dist/{chunk-index-014rjv9k.js → chunk-index-6tpr6r6d.js} +2 -2
- package/dist/{chunk-index-e5sd30xy.js → chunk-index-7bbqtdce.js} +2 -2
- package/dist/chunk-index-84v7j6vh.js +50 -0
- package/dist/chunk-index-8f9jer8r.js +2 -0
- package/dist/chunk-index-91z3g774.js +1 -0
- package/dist/{chunk-index-dhpt8qr9.js → chunk-index-934zgd18.js} +2 -2
- package/dist/{chunk-index-awx6nnta.js → chunk-index-9nents16.js} +2 -2
- package/dist/chunk-index-9w0sar6s.js +2 -0
- package/dist/chunk-index-a768zmp6.js +2 -0
- package/dist/{chunk-index-nqfkwkfp.js → chunk-index-b7jjca66.js} +2 -2
- package/dist/chunk-index-c5x70d9g.js +2 -0
- package/dist/chunk-index-cmxs5y22.js +42 -0
- package/dist/chunk-index-cwc1vvpw.js +2 -0
- package/dist/chunk-index-d85x96q0.js +2 -0
- package/dist/chunk-index-datrrs6b.js +2 -0
- package/dist/chunk-index-dep2e3f3.js +2 -0
- package/dist/chunk-index-dtste2ed.js +2 -0
- package/dist/chunk-index-ewj52be9.js +2 -0
- package/dist/{chunk-index-pcfhpm8v.js → chunk-index-f3b5nch2.js} +142 -18
- package/dist/{chunk-index-km40bhsz.js → chunk-index-frwa2fmc.js} +2 -2
- package/dist/chunk-index-grkczq2c.js +2 -0
- package/dist/chunk-index-gzvkpe3e.js +2 -0
- package/dist/chunk-index-kfv996qe.js +920 -0
- package/dist/chunk-index-kw85ajap.js +138 -0
- package/dist/chunk-index-satam151.js +2 -0
- package/dist/{chunk-index-y8rnq8zf.js → chunk-index-smw1n2pt.js} +5 -5
- package/dist/chunk-index-th43f690.js +2 -0
- package/dist/chunk-index-ths2hxpe.js +1 -0
- package/dist/{chunk-index-wnjp6wv0.js → chunk-index-tmx30r1w.js} +2 -2
- package/dist/{chunk-index-hssjbr1k.js → chunk-index-y00wke93.js} +2 -2
- package/dist/chunk-index-y4sqmenv.js +2 -0
- package/dist/{chunk-index-66nskrdj.js → chunk-index-yrhbhmxz.js} +2 -2
- package/dist/chunk-index-zd76pr2k.js +2 -0
- package/dist/chunk-lang-css-fa7c0zfj.js +1 -0
- package/dist/{chunk-lang-dockerfile-hrav55t7.js → chunk-lang-dockerfile-46rk929v.js} +1 -1
- package/dist/{chunk-lang-go-x9ahv6e5.js → chunk-lang-go-qb0qdpg7.js} +3 -3
- package/dist/chunk-lang-html-47mf2y0v.js +1 -0
- package/dist/chunk-lang-javascript-d32qrvxz.js +1 -0
- package/dist/{chunk-lang-json-hsemyd0r.js → chunk-lang-json-1q4fp694.js} +1 -1
- package/dist/{chunk-lang-markdown-zkwb4c9n.js → chunk-lang-markdown-5w7t28wa.js} +4 -4
- package/dist/{chunk-lang-python-tewx7006.js → chunk-lang-python-3dq0p4rf.js} +2 -2
- package/dist/{chunk-lang-rust-0ydx84s1.js → chunk-lang-rust-70pa0agr.js} +1 -1
- package/dist/{chunk-lang-shell-gq1tmdz4.js → chunk-lang-shell-4pxt7hf4.js} +1 -1
- package/dist/{chunk-lang-sql-xt1qyycg.js → chunk-lang-sql-kgcbh2a9.js} +2 -2
- package/dist/{chunk-lang-toml-0hamc5fc.js → chunk-lang-toml-99t95pab.js} +1 -1
- package/dist/chunk-lang-typescript-4k1d8z7s.js +1 -0
- package/dist/{chunk-lang-xml-wf6h5pt2.js → chunk-lang-xml-mnbv9yfa.js} +1 -1
- package/dist/{chunk-lang-yaml-7vk521h1.js → chunk-lang-yaml-2by9g1dy.js} +1 -1
- package/dist/chunk-layout-actions-cgghzj1w.js +1 -0
- package/dist/chunk-menu-items-24cfwawa.js +2 -0
- package/dist/chunk-mode-toggle-v4baz3r1.js +1 -0
- package/dist/chunk-note-row-72n6cqyk.js +75 -0
- package/dist/chunk-notes-83s3tsey.js +1 -0
- package/dist/chunk-notice-bell-a4ty5h1p.js +1 -0
- package/dist/chunk-notices-evny44be.js +1 -0
- package/dist/chunk-onboarding-q1avjbrx.js +1 -0
- package/dist/chunk-overview-42h45ehd.js +1 -0
- package/dist/chunk-palette-e44qepmh.js +1 -0
- package/dist/chunk-placement-items-v466t0sv.js +1 -0
- package/dist/chunk-profile-bqcgkgk4.js +1 -0
- package/dist/chunk-prompt-history-hgbtcn4b.js +1 -0
- package/dist/chunk-prompts-czs8y6r1.js +1 -0
- package/dist/chunk-room-confirms-h8cdj3ng.js +1 -0
- package/dist/chunk-room-menu-7s0feajw.js +1 -0
- package/dist/chunk-rooms-menu-button-gawqvpsr.js +1 -0
- package/dist/{chunk-schedules-2ft6xxyw.js → chunk-schedules-zba7krac.js} +1 -1
- package/dist/{chunk-seat-menu-d3npdtx1.js → chunk-seat-menu-ca5p4e9q.js} +1 -1
- package/dist/chunk-sheet-hosts-ssbz6hf2.js +1 -0
- package/dist/chunk-skill-editor-n0y9pd90.js +8 -0
- package/dist/chunk-skills-ea4vb2s4.js +1 -0
- package/dist/chunk-switcher-list-pj5csqr9.js +1 -0
- package/dist/chunk-tab-dot-4tkpxqeh.js +1 -0
- package/dist/chunk-table-j8nmtdnn.js +1 -0
- package/dist/chunk-table-seats-qeytqe0m.js +1 -0
- package/dist/chunk-table2d-18y029k9.js +1 -0
- package/dist/chunk-table3d-q2s31ae0.js +4220 -0
- package/dist/chunk-thread-actions-w3eadnh7.js +1 -0
- package/dist/chunk-thread-cards-fbpky3c9.js +1 -0
- package/dist/chunk-thread-list-pghe84be.js +1 -0
- package/dist/chunk-transcript-eqzxgqys.js +1 -0
- package/dist/chunk-where-ygyr4jdj.js +1 -0
- package/dist/chunk-workspace-h0p32ygy.js +578 -0
- package/dist/chunk-zh-tw-k6xx1hp2.js +1 -0
- package/dist/index.html +1 -2
- package/dist/sw.js +1 -1
- package/package.json +2 -2
- package/src/api-types.ts +63 -3
- package/src/app/api-http.ts +1 -1
- package/src/app/app.ts +5 -0
- package/src/app/room-log.ts +16 -4
- package/src/app/rooms.ts +39 -0
- package/src/app/routes.ts +96 -2
- package/src/app/socket.ts +45 -9
- package/src/app/surface-threads.ts +1 -1
- package/src/app/surface.ts +2 -1
- package/src/app/threads.ts +51 -2
- package/src/auth/authenticator.ts +18 -5
- package/src/auth/login-page.ts +4 -2
- package/src/auth/oidc-pages.ts +2 -0
- package/src/auth/oidc.ts +39 -1
- package/src/auth/sessions.ts +10 -3
- package/src/authz.ts +2 -1
- package/src/console-server.ts +32 -6
- package/src/host/cards.ts +18 -5
- package/src/host/context.ts +72 -25
- package/src/host/forms.ts +3 -0
- package/src/host/frames.ts +8 -1
- package/src/host/host-app.ts +6 -4
- package/src/host/host-routes.ts +36 -3
- package/src/host/room-views.ts +6 -6
- package/src/host/rooms-service.ts +16 -1
- package/src/host/views.ts +3 -0
- package/src/i18n/zh-tw.ts +103 -79
- package/src/index.ts +15 -1
- package/src/options.ts +37 -1
- package/src/page-presentation.ts +47 -2
- package/src/pwa.ts +32 -4
- package/src/web-app.ts +47 -3
- package/src/web-plugin.ts +32 -10
- package/dist/chunk-account-hw49gd0d.js +0 -1
- package/dist/chunk-action-menu-rm5cyqkj.js +0 -1
- package/dist/chunk-agent-b017vswv.js +0 -1
- package/dist/chunk-arranged-rooms-q5v3dm1r.js +0 -1
- package/dist/chunk-character-creator-5dxcvd16.js +0 -1
- package/dist/chunk-codeeditor-pyt09fwg.js +0 -2
- package/dist/chunk-confirm-521yqmta.js +0 -1
- package/dist/chunk-connectors-hrm06nx8.js +0 -1
- package/dist/chunk-conversations-e26rrf01.js +0 -1
- package/dist/chunk-entry-jxxb3ath.js +0 -1
- package/dist/chunk-folder-list-b97xk12b.js +0 -1
- package/dist/chunk-folds-b8q7eags.js +0 -1
- package/dist/chunk-group-scene-dx6wt19m.js +0 -1
- package/dist/chunk-group-table-e4jr8r3a.js +0 -1
- package/dist/chunk-host-state-g7e7c0k5.js +0 -1
- package/dist/chunk-host-ui-x7k6n07v.js +0 -939
- package/dist/chunk-index-2d0kg096.js +0 -2
- package/dist/chunk-index-2qd81wbx.js +0 -2
- package/dist/chunk-index-2stynxw6.css +0 -1
- package/dist/chunk-index-459drsft.js +0 -2
- package/dist/chunk-index-4mfj596y.js +0 -2
- package/dist/chunk-index-bbsytycb.js +0 -2
- package/dist/chunk-index-c07p8k0x.js +0 -4
- package/dist/chunk-index-cq1e9xdz.js +0 -2
- package/dist/chunk-index-d1prgfbp.js +0 -2
- package/dist/chunk-index-g351921r.js +0 -1
- package/dist/chunk-index-k2qq1gdj.js +0 -2
- package/dist/chunk-index-ke9ynm0n.js +0 -2
- package/dist/chunk-index-kpscvg85.js +0 -2
- package/dist/chunk-index-m0d100mg.js +0 -2
- package/dist/chunk-index-mb72snk9.js +0 -2
- package/dist/chunk-index-mjqqver5.js +0 -4
- package/dist/chunk-index-nwqk447w.js +0 -2
- package/dist/chunk-index-q8h8xzbm.js +0 -2
- package/dist/chunk-index-r5vszgfs.js +0 -2
- package/dist/chunk-index-rjv7vrwd.js +0 -13
- package/dist/chunk-index-v23m38p6.js +0 -50
- package/dist/chunk-index-va0g7d88.js +0 -1
- package/dist/chunk-index-xgkd15n3.js +0 -2
- package/dist/chunk-index-z3k18atc.js +0 -2
- package/dist/chunk-lang-css-6bepqpnx.js +0 -1
- package/dist/chunk-lang-html-8jc72zt4.js +0 -1
- package/dist/chunk-lang-javascript-2p2qtbnf.js +0 -1
- package/dist/chunk-lang-typescript-82xf7z2e.js +0 -1
- package/dist/chunk-layout-actions-zh7bezk7.js +0 -1
- package/dist/chunk-menu-items-1n9qargb.js +0 -2
- package/dist/chunk-notes-t15bh04m.js +0 -1
- package/dist/chunk-notice-bell-w3y0h0sr.js +0 -1
- package/dist/chunk-notices-fa3g7qze.js +0 -1
- package/dist/chunk-onboarding-34v3nr17.js +0 -1
- package/dist/chunk-overview-km5vq375.js +0 -1
- package/dist/chunk-palette-a8txdgd0.js +0 -1
- package/dist/chunk-placement-items-wgaxsbvs.js +0 -1
- package/dist/chunk-profile-ypxdbsk1.js +0 -1
- package/dist/chunk-prompt-history-wm9dsy6s.js +0 -1
- package/dist/chunk-prompts-7mhhq7zs.js +0 -1
- package/dist/chunk-room-confirms-t11a1gsw.js +0 -1
- package/dist/chunk-room-menu-2tm2xtfx.js +0 -1
- package/dist/chunk-rooms-menu-button-wxnsxjwc.js +0 -1
- package/dist/chunk-sheet-hosts-vngvp7wv.js +0 -1
- package/dist/chunk-skill-editor-65n2hsq4.js +0 -8
- package/dist/chunk-skills-shkdwes8.js +0 -1
- package/dist/chunk-switcher-list-g6hhmr1t.js +0 -1
- package/dist/chunk-table-1b6bsq2y.js +0 -1
- package/dist/chunk-table-seats-vy1qhdks.js +0 -1
- package/dist/chunk-table2d-n26b7gd0.js +0 -1
- package/dist/chunk-table3d-8aysyf56.js +0 -4220
- package/dist/chunk-transcript-355faceq.js +0 -1
- package/dist/chunk-where-j5qdt8a4.js +0 -1
- package/dist/chunk-workspace-vrbs6zx1.js +0 -1
- package/dist/chunk-zh-tw-zeg600bk.js +0 -1
package/src/host/context.ts
CHANGED
|
@@ -54,22 +54,46 @@ function sourceOf(message: RoomMessage): Source {
|
|
|
54
54
|
};
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
+
/** What `WebApp.rooms.context` takes. */
|
|
58
|
+
export interface HostRoomContextOptions {
|
|
59
|
+
/** The most messages to return, newest kept; default 15, at most 100. */
|
|
60
|
+
limit?: number;
|
|
61
|
+
/** The most characters of one message before it is cut with `…`; default 500. */
|
|
62
|
+
messageChars?: number;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/** The two ways a host room's recent messages are read. */
|
|
66
|
+
export interface HostRoomContext {
|
|
67
|
+
/** For a turn the room's message addressed: marks what it returns as handed over. */
|
|
68
|
+
read(
|
|
69
|
+
message: InboundMessage,
|
|
70
|
+
options?: ChannelContextOptions,
|
|
71
|
+
): Promise<ChannelContext | undefined>;
|
|
72
|
+
/** For a host with no addressing message: the same block, and nothing is marked handed over. */
|
|
73
|
+
peek(
|
|
74
|
+
room: string,
|
|
75
|
+
options?: HostRoomContextOptions,
|
|
76
|
+
): Promise<ChannelContext | undefined>;
|
|
77
|
+
}
|
|
78
|
+
|
|
57
79
|
/**
|
|
58
|
-
* The app surface's
|
|
80
|
+
* The app surface's channel context of a host room: what was said in it since the assistant
|
|
59
81
|
* last posted, before the message that addressed it, as the core's block of channel messages. An
|
|
60
82
|
* outsider's words read as `external`, a notice line is not in the channel as far as the context
|
|
61
83
|
* goes, and a message already handed to the conversation is not handed again. Anything but a host
|
|
62
|
-
* room has none: an agent room is one person's.
|
|
84
|
+
* room has none: an agent room is one person's. `peek` reads the same block for the end of the
|
|
85
|
+
* room and leaves the handed-over marks alone, so a retry gets the same.
|
|
63
86
|
*/
|
|
64
|
-
export function
|
|
87
|
+
export function hostRoomContext(
|
|
65
88
|
ctx: Pick<HostContext, "registry" | "log">,
|
|
66
|
-
):
|
|
67
|
-
message: InboundMessage,
|
|
68
|
-
options?: ChannelContextOptions,
|
|
69
|
-
) => Promise<ChannelContext | undefined> {
|
|
89
|
+
): HostRoomContext {
|
|
70
90
|
const seen = new Map<string, Set<string>>();
|
|
71
|
-
|
|
72
|
-
|
|
91
|
+
const build = async (
|
|
92
|
+
room: string,
|
|
93
|
+
addressed: number | undefined,
|
|
94
|
+
options: ChannelContextOptions,
|
|
95
|
+
consume: boolean,
|
|
96
|
+
): Promise<ChannelContext | undefined> => {
|
|
73
97
|
if (!ctx.registry.isHostRoom(room)) return undefined;
|
|
74
98
|
const fetch = positive("fetch", options.fetch ?? DEFAULT_FETCH, PAGE_LIMIT);
|
|
75
99
|
const keep = positive("keep", options.keep ?? DEFAULT_KEEP, 100);
|
|
@@ -78,12 +102,7 @@ export function hostChannelContext(
|
|
|
78
102
|
options.messageChars ?? DEFAULT_CHARS,
|
|
79
103
|
20_000,
|
|
80
104
|
);
|
|
81
|
-
const
|
|
82
|
-
const page = await ctx.log.page(
|
|
83
|
-
room,
|
|
84
|
-
addressed === undefined ? undefined : Number(addressed),
|
|
85
|
-
fetch,
|
|
86
|
-
);
|
|
105
|
+
const page = await ctx.log.page(room, addressed, fetch);
|
|
87
106
|
const sources = page.messages.map((one) => ({
|
|
88
107
|
source: sourceOf(one),
|
|
89
108
|
notice: authorKindOf(one) === "notice",
|
|
@@ -99,24 +118,26 @@ export function hostChannelContext(
|
|
|
99
118
|
const lastEnd = considered.findLastIndex(
|
|
100
119
|
(entry) => verdict(entry) === "end",
|
|
101
120
|
);
|
|
102
|
-
const known = seen.get(room) ?? new Set<string>();
|
|
121
|
+
const known = consume ? (seen.get(room) ?? new Set<string>()) : undefined;
|
|
103
122
|
const fresh = considered
|
|
104
123
|
.slice(lastEnd + 1)
|
|
105
124
|
.filter(
|
|
106
125
|
(entry) =>
|
|
107
126
|
verdict(entry) === "keep" &&
|
|
108
|
-
!known
|
|
127
|
+
!known?.has(entry.source.id) &&
|
|
109
128
|
entry.source.text !== "",
|
|
110
129
|
)
|
|
111
130
|
.slice(-keep);
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
131
|
+
if (known) {
|
|
132
|
+
// What was in the window is not handed over again, kept or not.
|
|
133
|
+
for (const entry of considered.slice(lastEnd + 1))
|
|
134
|
+
known.add(entry.source.id);
|
|
135
|
+
seen.delete(room);
|
|
136
|
+
seen.set(room, new Set([...known].slice(-SEEN_PER_ROOM)));
|
|
137
|
+
if (seen.size > SEEN_ROOMS) {
|
|
138
|
+
const oldest = seen.keys().next().value;
|
|
139
|
+
if (oldest !== undefined) seen.delete(oldest);
|
|
140
|
+
}
|
|
120
141
|
}
|
|
121
142
|
if (fresh.length === 0) return undefined;
|
|
122
143
|
const messages: ChannelContextMessage[] = fresh.map(({ source }) => ({
|
|
@@ -138,4 +159,30 @@ export function hostChannelContext(
|
|
|
138
159
|
}));
|
|
139
160
|
return { messages, text: formatChannelContext(messages) };
|
|
140
161
|
};
|
|
162
|
+
return {
|
|
163
|
+
read(message, options = {}) {
|
|
164
|
+
const addressed = /^app-(\d+)$/.exec(message.messageId)?.[1];
|
|
165
|
+
return build(
|
|
166
|
+
message.channel,
|
|
167
|
+
addressed === undefined ? undefined : Number(addressed),
|
|
168
|
+
options,
|
|
169
|
+
true,
|
|
170
|
+
);
|
|
171
|
+
},
|
|
172
|
+
peek(room, options = {}) {
|
|
173
|
+
const limit = options.limit ?? DEFAULT_KEEP;
|
|
174
|
+
return build(
|
|
175
|
+
room,
|
|
176
|
+
undefined,
|
|
177
|
+
{
|
|
178
|
+
keep: limit,
|
|
179
|
+
fetch: Math.min(PAGE_LIMIT, Math.max(DEFAULT_FETCH, limit * 3)),
|
|
180
|
+
...(options.messageChars === undefined
|
|
181
|
+
? {}
|
|
182
|
+
: { messageChars: options.messageChars }),
|
|
183
|
+
},
|
|
184
|
+
false,
|
|
185
|
+
);
|
|
186
|
+
},
|
|
187
|
+
};
|
|
141
188
|
}
|
package/src/host/forms.ts
CHANGED
|
@@ -125,6 +125,7 @@ export function checkCard(card: {
|
|
|
125
125
|
state?: string | undefined;
|
|
126
126
|
links?: readonly { label: string; url: string }[] | undefined;
|
|
127
127
|
buttons?: readonly HostAction[] | undefined;
|
|
128
|
+
keep?: boolean | undefined;
|
|
128
129
|
}): void {
|
|
129
130
|
if (typeof card.title !== "string" || card.title.length > LIMITS.title)
|
|
130
131
|
fail(`a card's title is at most ${LIMITS.title} characters`);
|
|
@@ -134,6 +135,8 @@ export function checkCard(card: {
|
|
|
134
135
|
fail(`a card's tone is note, ok, warn or risk`);
|
|
135
136
|
if (card.state !== undefined && !STATES.has(card.state))
|
|
136
137
|
fail(`a card's state is open, working, done, superseded or expired`);
|
|
138
|
+
if (card.keep !== undefined && typeof card.keep !== "boolean")
|
|
139
|
+
fail("a card's keep is true or false");
|
|
137
140
|
const links = card.links ?? [];
|
|
138
141
|
if (links.length > LIMITS.links)
|
|
139
142
|
fail(`a card takes at most ${LIMITS.links} links`);
|
package/src/host/frames.ts
CHANGED
|
@@ -60,7 +60,7 @@ export async function actInRoom(
|
|
|
60
60
|
if (press.card !== undefined) {
|
|
61
61
|
const card = await ctx.cards.get(room, press.card);
|
|
62
62
|
if (!card) return { refused: "unknown" };
|
|
63
|
-
offered = buttonsFor(viewer, found, access, card);
|
|
63
|
+
offered = await buttonsFor(viewer, found, access, card);
|
|
64
64
|
} else {
|
|
65
65
|
const header = await found.kind
|
|
66
66
|
.header?.(viewer, found.id)
|
|
@@ -146,6 +146,13 @@ export function installFrames(ctx: HostContext): void {
|
|
|
146
146
|
connection,
|
|
147
147
|
audience: { room: frame.room },
|
|
148
148
|
});
|
|
149
|
+
// So is a turn already running: a seat at the round table shows it from the first frame, not after the next change.
|
|
150
|
+
const activity = ctx.app.activityOf(asRoom(frame.room));
|
|
151
|
+
if (activity.typing)
|
|
152
|
+
ctx.sockets.deliver(
|
|
153
|
+
{ type: "activity", room: frame.room, ...activity },
|
|
154
|
+
{ connection, audience: { room: frame.room } },
|
|
155
|
+
);
|
|
149
156
|
},
|
|
150
157
|
);
|
|
151
158
|
|
package/src/host/host-app.ts
CHANGED
|
@@ -19,7 +19,7 @@ import type { Uploads } from "../app/uploads.ts";
|
|
|
19
19
|
import type { Viewer, WebAppRegistry } from "../web-app.ts";
|
|
20
20
|
import { HostAccess } from "./access.ts";
|
|
21
21
|
import type { HostCardLog } from "./cards.ts";
|
|
22
|
-
import {
|
|
22
|
+
import { type HostRoomContext, hostRoomContext } from "./context.ts";
|
|
23
23
|
import { installFrames } from "./frames.ts";
|
|
24
24
|
import type { HostContext } from "./host-context.ts";
|
|
25
25
|
import { hostArea, pagesArea } from "./host-routes.ts";
|
|
@@ -66,7 +66,7 @@ export class HostApp {
|
|
|
66
66
|
readonly rooms: HostRoomsService;
|
|
67
67
|
readonly pages: HostPagesService;
|
|
68
68
|
readonly #context: HostContext;
|
|
69
|
-
readonly #
|
|
69
|
+
readonly #roomContext: HostRoomContext;
|
|
70
70
|
|
|
71
71
|
constructor(options: HostAppOptions) {
|
|
72
72
|
const now = options.now ?? Date.now;
|
|
@@ -102,6 +102,8 @@ export class HostApp {
|
|
|
102
102
|
maxFileBytes: options.maxFileBytes,
|
|
103
103
|
roomUrl: options.roomUrl,
|
|
104
104
|
listed,
|
|
105
|
+
context: (room, contextOptions) =>
|
|
106
|
+
this.#roomContext.peek(room, contextOptions),
|
|
105
107
|
now,
|
|
106
108
|
logger: options.logger,
|
|
107
109
|
});
|
|
@@ -126,7 +128,7 @@ export class HostApp {
|
|
|
126
128
|
now,
|
|
127
129
|
logger: options.logger,
|
|
128
130
|
};
|
|
129
|
-
this.#
|
|
131
|
+
this.#roomContext = hostRoomContext(this.#context);
|
|
130
132
|
}
|
|
131
133
|
|
|
132
134
|
/** Plugs the pieces into the registry: who reads, the frames, the areas, and the services a host reaches. */
|
|
@@ -153,7 +155,7 @@ export class HostApp {
|
|
|
153
155
|
message: InboundMessage,
|
|
154
156
|
options?: ChannelContextOptions,
|
|
155
157
|
): Promise<ChannelContext | undefined> {
|
|
156
|
-
return this.#
|
|
158
|
+
return this.#roomContext.read(message, options);
|
|
157
159
|
}
|
|
158
160
|
|
|
159
161
|
/** Whether a stored file bound to a room or a page may be served to this viewer now. */
|
package/src/host/host-routes.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { HostCardView, RoomMessage } from "../api-types.ts";
|
|
1
|
+
import type { HostCardView, RoomMessage, SendWording } from "../api-types.ts";
|
|
2
2
|
import { bad, bodyOf, HttpError } from "../app/api-http.ts";
|
|
3
3
|
import { PAGE_LIMIT } from "../app/room-log.ts";
|
|
4
4
|
import { json } from "../http.ts";
|
|
@@ -244,14 +244,15 @@ async function open(
|
|
|
244
244
|
ctx.log.page(room, undefined, OPEN_LIMIT),
|
|
245
245
|
ctx.cards.list(room),
|
|
246
246
|
]);
|
|
247
|
-
const cards: HostCardView[] =
|
|
248
|
-
cardView(viewer, found, access, card),
|
|
247
|
+
const cards: HostCardView[] = await Promise.all(
|
|
248
|
+
stored.map((card) => cardView(viewer, found, access, card)),
|
|
249
249
|
);
|
|
250
250
|
return {
|
|
251
251
|
room,
|
|
252
252
|
kind: found.kind.kind,
|
|
253
253
|
id: found.id,
|
|
254
254
|
access: { send: [...access.send], act: access.act },
|
|
255
|
+
...wordingOf(found.kind.sendWording),
|
|
255
256
|
header,
|
|
256
257
|
messages: withExpiredFiles(page.messages, ctx.files),
|
|
257
258
|
more: page.more,
|
|
@@ -261,6 +262,38 @@ async function open(
|
|
|
261
262
|
};
|
|
262
263
|
}
|
|
263
264
|
|
|
265
|
+
/** The longest label and placeholder a kind may give, in characters. */
|
|
266
|
+
const LABEL_CHARS = 40;
|
|
267
|
+
const PLACEHOLDER_CHARS = 100;
|
|
268
|
+
|
|
269
|
+
const trimmed = (value: unknown, chars: number): string | undefined =>
|
|
270
|
+
typeof value === "string"
|
|
271
|
+
? Array.from(value.trim()).slice(0, chars).join("") || undefined
|
|
272
|
+
: undefined;
|
|
273
|
+
|
|
274
|
+
/** The kind's wording of the two ways to write as the page may show it: text only, cut short; nothing when it gave none. */
|
|
275
|
+
function wordingOf(wording: SendWording | undefined): {
|
|
276
|
+
wording?: SendWording;
|
|
277
|
+
} {
|
|
278
|
+
const note = trimmed(wording?.note?.placeholder, PLACEHOLDER_CHARS);
|
|
279
|
+
const label = trimmed(wording?.turn?.label, LABEL_CHARS);
|
|
280
|
+
const turn = trimmed(wording?.turn?.placeholder, PLACEHOLDER_CHARS);
|
|
281
|
+
if (!note && !label && !turn) return {};
|
|
282
|
+
return {
|
|
283
|
+
wording: {
|
|
284
|
+
...(note ? { note: { placeholder: note } } : {}),
|
|
285
|
+
...(label || turn
|
|
286
|
+
? {
|
|
287
|
+
turn: {
|
|
288
|
+
...(label ? { label } : {}),
|
|
289
|
+
...(turn ? { placeholder: turn } : {}),
|
|
290
|
+
},
|
|
291
|
+
}
|
|
292
|
+
: {}),
|
|
293
|
+
},
|
|
294
|
+
};
|
|
295
|
+
}
|
|
296
|
+
|
|
264
297
|
async function older(
|
|
265
298
|
ctx: HostContext,
|
|
266
299
|
viewer: Viewer,
|
package/src/host/room-views.ts
CHANGED
|
@@ -77,27 +77,27 @@ export interface FoundRoom {
|
|
|
77
77
|
* The buttons of a host card this viewer may press: none once the card is settled, none without
|
|
78
78
|
* `access.act`, and otherwise the card's own, narrowed by the kind's `buttons` when it has one.
|
|
79
79
|
*/
|
|
80
|
-
export function buttonsFor(
|
|
80
|
+
export async function buttonsFor(
|
|
81
81
|
viewer: Viewer,
|
|
82
82
|
found: FoundRoom,
|
|
83
83
|
access: RoomAccess,
|
|
84
84
|
card: StoredHostCard,
|
|
85
|
-
): NonNullable<StoredHostCard["buttons"]
|
|
85
|
+
): Promise<NonNullable<StoredHostCard["buttons"]>> {
|
|
86
86
|
const all = card.buttons ?? [];
|
|
87
87
|
if (!access.act || !pressable(card) || all.length === 0) return [];
|
|
88
88
|
if (!found.kind.buttons) return all;
|
|
89
|
-
const allowed = new Set(found.kind.buttons(viewer, found.id, card));
|
|
89
|
+
const allowed = new Set(await found.kind.buttons(viewer, found.id, card));
|
|
90
90
|
return all.filter((button) => allowed.has(button.id));
|
|
91
91
|
}
|
|
92
92
|
|
|
93
93
|
/** A host card as one viewer sees it. */
|
|
94
|
-
export function cardView(
|
|
94
|
+
export async function cardView(
|
|
95
95
|
viewer: Viewer,
|
|
96
96
|
found: FoundRoom,
|
|
97
97
|
access: RoomAccess,
|
|
98
98
|
card: StoredHostCard,
|
|
99
|
-
): HostCardView {
|
|
100
|
-
const buttons = buttonsFor(viewer, found, access, card);
|
|
99
|
+
): Promise<HostCardView> {
|
|
100
|
+
const buttons = await buttonsFor(viewer, found, access, card);
|
|
101
101
|
return {
|
|
102
102
|
id: card.id,
|
|
103
103
|
room: card.room,
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ChannelKey, Logger } from "pi-roundtable";
|
|
2
2
|
import { PluginError } from "pi-roundtable";
|
|
3
|
+
import type { ChannelContext } from "pi-roundtable/discord";
|
|
3
4
|
import type { ServerFrame } from "../api-types.ts";
|
|
4
5
|
import { MAX_MESSAGE_CHARS } from "../api-types.ts";
|
|
5
6
|
import { type FileDir, isFileId, safeName } from "../app/files.ts";
|
|
@@ -15,6 +16,7 @@ import type {
|
|
|
15
16
|
} from "../web-app.ts";
|
|
16
17
|
import type { HostAccess } from "./access.ts";
|
|
17
18
|
import type { HostCardLog } from "./cards.ts";
|
|
19
|
+
import type { HostRoomContextOptions } from "./context.ts";
|
|
18
20
|
import { checkCard } from "./forms.ts";
|
|
19
21
|
import { cardView, checkPicture, type FoundRoom } from "./room-views.ts";
|
|
20
22
|
|
|
@@ -37,6 +39,11 @@ export interface HostRoomsOptions {
|
|
|
37
39
|
roomUrl(room: ChannelKey, message?: number): string;
|
|
38
40
|
/** Which kinds a person's last list showed, so the list's changes reach only them. */
|
|
39
41
|
listed: ListedKinds;
|
|
42
|
+
/** A room's recent messages as a channel-context block, without marking them handed over. */
|
|
43
|
+
context(
|
|
44
|
+
room: string,
|
|
45
|
+
options?: HostRoomContextOptions,
|
|
46
|
+
): Promise<ChannelContext | undefined>;
|
|
40
47
|
now?: () => number;
|
|
41
48
|
logger: Logger;
|
|
42
49
|
}
|
|
@@ -272,6 +279,14 @@ export class HostRoomsService implements HostRooms {
|
|
|
272
279
|
return this.#options.log.byReference(room, reference);
|
|
273
280
|
}
|
|
274
281
|
|
|
282
|
+
async context(
|
|
283
|
+
room: ChannelKey,
|
|
284
|
+
options?: HostRoomContextOptions,
|
|
285
|
+
): Promise<ChannelContext | undefined> {
|
|
286
|
+
this.#found(room);
|
|
287
|
+
return this.#options.context(room, options);
|
|
288
|
+
}
|
|
289
|
+
|
|
275
290
|
// ── Cards ──
|
|
276
291
|
|
|
277
292
|
async card(room: ChannelKey, card: HostCard): Promise<{ id: string }> {
|
|
@@ -313,7 +328,7 @@ export class HostRoomsService implements HostRooms {
|
|
|
313
328
|
{
|
|
314
329
|
type: "host_card",
|
|
315
330
|
room,
|
|
316
|
-
card: cardView(viewer, found, access, stored),
|
|
331
|
+
card: await cardView(viewer, found, access, stored),
|
|
317
332
|
},
|
|
318
333
|
{ connection, audience: { room } },
|
|
319
334
|
);
|
package/src/host/views.ts
CHANGED
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
PictureRef,
|
|
9
9
|
ReadMarkView,
|
|
10
10
|
RoomMessage,
|
|
11
|
+
SendWording,
|
|
11
12
|
} from "../api-types.ts";
|
|
12
13
|
|
|
13
14
|
/** One room of a kind, as the list shows it. */
|
|
@@ -47,6 +48,8 @@ export interface HostRoomView {
|
|
|
47
48
|
id: string;
|
|
48
49
|
/** What this viewer may do in the room. */
|
|
49
50
|
access: { send: ("note" | "turn")[]; act: boolean };
|
|
51
|
+
/** The kind's own wording of the two ways to write, where it gave any. */
|
|
52
|
+
wording?: SendWording;
|
|
50
53
|
header: HostHeaderView;
|
|
51
54
|
/** The newest messages, oldest first. */
|
|
52
55
|
messages: RoomMessage[];
|