pi-roundtable-web 0.26.1 → 0.26.3
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 +24 -0
- package/README.md +25 -6
- package/dist/chunk-account-hck7mhf8.js +1 -0
- package/dist/chunk-action-menu-beqh40k6.js +1 -0
- package/dist/{chunk-agent-rdv7ekrt.js → chunk-agent-znnth24t.js} +1 -1
- package/dist/chunk-arranged-rooms-wfakrddk.js +1 -0
- package/dist/chunk-bulk-actions-c4tkvk9g.js +1 -0
- package/dist/chunk-character-creator-qwcsdytw.js +1 -0
- package/dist/{chunk-codeeditor-ajhgd33t.js → chunk-codeeditor-6xavqntt.js} +2 -2
- package/dist/chunk-confirm-z95j9jt3.js +1 -0
- package/dist/chunk-connectors-7xqsg5q5.js +1 -0
- package/dist/chunk-conversations-zc4mv6vj.js +1 -0
- package/dist/{chunk-drag-peyb8t0k.js → chunk-drag-0mefcvp3.js} +1 -1
- package/dist/chunk-entry-hrt97qed.js +1 -0
- package/dist/chunk-folder-list-nemtqbnc.js +1 -0
- package/dist/chunk-folds-xpg9qcwf.js +1 -0
- package/dist/chunk-gate-86r8a2vf.js +1 -0
- package/dist/chunk-group-scene-g24kxz6f.js +1 -0
- package/dist/chunk-group-table-851jm8ef.js +1 -0
- package/dist/chunk-host-icons-23zhqpmc.js +1 -0
- package/dist/chunk-host-picture-8tnh2wam.js +1 -0
- package/dist/chunk-host-state-j41sp065.js +1 -0
- package/dist/chunk-host-ui-cje7a9t9.js +1 -0
- package/dist/chunk-index-1fn8q4ps.js +2 -0
- package/dist/{chunk-index-73gh7kgr.js → chunk-index-30vw4nhh.js} +35 -3
- package/dist/{chunk-index-k7t14rwa.js → chunk-index-3aa5czxj.js} +2 -2
- package/dist/chunk-index-3k7sdph2.js +1 -0
- package/dist/{chunk-index-w31c4yjv.js → chunk-index-3wbyq10m.js} +2 -2
- package/dist/{chunk-index-vcyzwtk2.js → chunk-index-47yspxa4.js} +2 -2
- package/dist/chunk-index-4jf4jvfd.css +1 -0
- package/dist/chunk-index-4s1qj9we.js +2 -0
- package/dist/chunk-index-5fz5e1ct.js +2 -0
- package/dist/{chunk-index-5yzfrf7b.js → chunk-index-67p2sc14.js} +3 -3
- package/dist/{chunk-index-gyary7np.js → chunk-index-6c8mewwy.js} +12 -12
- package/dist/{chunk-index-yzsgsvnp.js → chunk-index-84wbc80d.js} +2 -2
- package/dist/{chunk-index-snzzfhk3.js → chunk-index-8eexybcx.js} +2 -2
- package/dist/{chunk-index-pk1yxkv4.js → chunk-index-agrvqf5b.js} +2 -2
- package/dist/chunk-index-azrnzgev.js +2 -0
- package/dist/{chunk-index-0xh24y25.js → chunk-index-c2ag3qw2.js} +2 -2
- package/dist/{chunk-index-kwcx62wb.js → chunk-index-e90kztkx.js} +3 -3
- package/dist/chunk-index-efgm0066.js +2 -0
- package/dist/{chunk-index-w2j3njb6.js → chunk-index-eh0wb6g0.js} +2 -2
- package/dist/{chunk-index-z9x4cnzf.js → chunk-index-eyc2rkq5.js} +2 -2
- package/dist/chunk-index-fe0m5mht.js +2 -0
- package/dist/{chunk-index-2yebd9tb.js → chunk-index-gt42wjby.js} +4 -4
- package/dist/{chunk-index-cvbdf2ca.js → chunk-index-hc0dgmrs.js} +2 -2
- package/dist/{chunk-index-eg55mqqe.js → chunk-index-hczrjnky.js} +1 -1
- package/dist/chunk-index-hrtyanda.js +2 -0
- package/dist/chunk-index-j479p62w.js +2 -0
- package/dist/chunk-index-jq970hsd.js +42 -0
- package/dist/chunk-index-kankja6p.js +50 -0
- package/dist/chunk-index-kb44jp50.js +2 -0
- package/dist/{chunk-index-m7fvw92f.js → chunk-index-m65cv329.js} +2 -2
- package/dist/chunk-index-mnwdbb4n.js +2 -0
- package/dist/chunk-index-n6zrc05a.js +2 -0
- package/dist/chunk-index-n93wqszt.js +8 -0
- package/dist/{chunk-index-zrvg23jd.js → chunk-index-nrexxkar.js} +2 -2
- package/dist/chunk-index-pgxgq61x.js +2 -0
- package/dist/chunk-index-q5vzjqr5.js +2 -0
- package/dist/chunk-index-qqwk157d.js +2 -0
- package/dist/{chunk-index-czdketze.js → chunk-index-r1v895tj.js} +2 -2
- package/dist/chunk-index-r3tqhew8.js +2 -0
- package/dist/chunk-index-raf772z6.js +2 -0
- package/dist/{chunk-index-dt9775ka.js → chunk-index-rcxz5w5d.js} +5 -5
- package/dist/chunk-index-rg9vh59e.js +1 -0
- package/dist/chunk-index-sxq69p1f.js +2 -0
- package/dist/chunk-index-taq2psr6.js +2 -0
- package/dist/{chunk-index-rmxbdmn2.js → chunk-index-tsxsy4qs.js} +2 -2
- package/dist/chunk-index-wc83vb0a.js +2 -0
- package/dist/chunk-index-xbs8q4kn.js +2 -0
- package/dist/chunk-index-xxbveevc.js +2 -0
- package/dist/{chunk-index-0fmmvgee.js → chunk-index-zx6yka30.js} +2 -2
- package/dist/chunk-lang-css-yrc25qk9.js +1 -0
- package/dist/{chunk-lang-dockerfile-k728ewsf.js → chunk-lang-dockerfile-tjxsnhr9.js} +1 -1
- package/dist/{chunk-lang-go-wjwgay4v.js → chunk-lang-go-drmfa25k.js} +3 -3
- package/dist/chunk-lang-html-rwe8epvf.js +1 -0
- package/dist/chunk-lang-javascript-d6x6qjtg.js +1 -0
- package/dist/{chunk-lang-json-7gd89mvs.js → chunk-lang-json-790swysy.js} +1 -1
- package/dist/{chunk-lang-markdown-b6qbe8bt.js → chunk-lang-markdown-483dkxcq.js} +5 -5
- package/dist/{chunk-lang-python-3v56y31b.js → chunk-lang-python-v9s40bzj.js} +2 -2
- package/dist/{chunk-lang-rust-kh0z74d1.js → chunk-lang-rust-z4gry1bf.js} +1 -1
- package/dist/{chunk-lang-shell-9zv0zvbx.js → chunk-lang-shell-egsvk8rj.js} +1 -1
- package/dist/{chunk-lang-sql-yw6bf3t7.js → chunk-lang-sql-75j3denr.js} +2 -2
- package/dist/{chunk-lang-toml-tg1r96nq.js → chunk-lang-toml-7dbjvhex.js} +1 -1
- package/dist/chunk-lang-typescript-1rv9vdcz.js +1 -0
- package/dist/{chunk-lang-xml-ev1mndd4.js → chunk-lang-xml-jg1sv4h2.js} +1 -1
- package/dist/{chunk-lang-yaml-fz5qpb3r.js → chunk-lang-yaml-g3v7s5je.js} +1 -1
- package/dist/chunk-layout-actions-6assr93n.js +1 -0
- package/dist/chunk-menu-items-9szcza00.js +2 -0
- package/dist/chunk-mode-toggle-bzbq7ycq.js +1 -0
- package/dist/{chunk-note-row-kd0fy7ee.js → chunk-note-row-g20h11pm.js} +2 -2
- package/dist/chunk-notes-gc8bssxn.js +1 -0
- package/dist/chunk-notice-bell-jehad6sx.js +1 -0
- package/dist/chunk-notices-prqkv7ge.js +1 -0
- package/dist/chunk-onboarding-mdtc2mb7.js +1 -0
- package/dist/chunk-overview-2j1s6kqg.js +1 -0
- package/dist/chunk-palette-4qt6ewem.js +1 -0
- package/dist/chunk-placement-items-2eect825.js +1 -0
- package/dist/chunk-profile-pfzyc04p.js +1 -0
- package/dist/chunk-prompt-history-2bcp2rky.js +1 -0
- package/dist/chunk-prompts-052mb05y.js +1 -0
- package/dist/chunk-room-confirms-asvmjh5s.js +1 -0
- package/dist/chunk-room-menu-dm2r2hk6.js +1 -0
- package/dist/chunk-rooms-menu-button-32hemzxc.js +1 -0
- package/dist/chunk-schedules-64h9gfwn.js +1 -0
- package/dist/{chunk-seat-menu-ys5v9abz.js → chunk-seat-menu-vrpc40v9.js} +1 -1
- package/dist/chunk-sheet-hosts-jaqx490x.js +1 -0
- package/dist/chunk-shortcut-help-bdbkdcs9.js +26 -0
- package/dist/chunk-shortcuts-qft33r8t.js +1 -0
- package/dist/chunk-skill-editor-z3dx2895.js +8 -0
- package/dist/chunk-skills-4jfcv39n.js +1 -0
- package/dist/chunk-switcher-list-xysjd4tw.js +1 -0
- package/dist/{chunk-tab-dot-r6brdbmw.js → chunk-tab-dot-1p68ybf8.js} +1 -1
- package/dist/chunk-table-0v4q4dx3.js +1 -0
- package/dist/chunk-table-seats-f683hx8k.js +1 -0
- package/dist/chunk-table2d-1a6atjhj.js +1 -0
- package/dist/chunk-table3d-k0ffeffj.js +4220 -0
- package/dist/chunk-thread-actions-4rpfg4fq.js +1 -0
- package/dist/chunk-thread-cards-t794fp4d.js +1 -0
- package/dist/chunk-thread-list-1pcygpka.js +1 -0
- package/dist/chunk-transcript-km4e3aey.js +1 -0
- package/dist/chunk-where-phjwqggy.js +1 -0
- package/dist/chunk-workspace-d064v56e.js +578 -0
- package/dist/chunk-zh-tw-qf18hcxc.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 +27 -0
- package/src/app/api-http.ts +1 -1
- package/src/app/routes.ts +158 -3
- package/src/app/surface-threads.ts +1 -1
- package/src/app/surface.ts +2 -1
- package/src/app/threads.ts +31 -0
- package/src/auth/authenticator.ts +8 -1
- package/src/auth/sessions.ts +10 -3
- package/src/host/host-app.ts +15 -1
- package/src/host/read-all.ts +80 -0
- package/src/host/reads.ts +67 -0
- package/src/i18n/zh-tw.ts +110 -79
- package/src/options.ts +1 -1
- package/src/web-plugin.ts +12 -1
- package/dist/chunk-account-ghq105t4.js +0 -1
- package/dist/chunk-action-menu-me0g9qsc.js +0 -1
- package/dist/chunk-arranged-rooms-81rqhx9x.js +0 -1
- package/dist/chunk-character-creator-bd426yjw.js +0 -1
- package/dist/chunk-confirm-p3mts8mx.js +0 -1
- package/dist/chunk-connectors-bkv3xn83.js +0 -1
- package/dist/chunk-conversations-0e0vbxwr.js +0 -1
- package/dist/chunk-entry-mv7q8br0.js +0 -1
- package/dist/chunk-folder-list-era3tksh.js +0 -1
- package/dist/chunk-folds-hvws0bd5.js +0 -1
- package/dist/chunk-gate-hc7dd0fx.js +0 -1
- package/dist/chunk-group-scene-dkbpmbh5.js +0 -1
- package/dist/chunk-group-table-fqmxgdfw.js +0 -1
- package/dist/chunk-host-icons-4xde9ntk.js +0 -1
- package/dist/chunk-host-picture-014yzk20.js +0 -1
- package/dist/chunk-host-state-t56e5sdy.js +0 -1
- package/dist/chunk-host-ui-p1zy9nmp.js +0 -1
- package/dist/chunk-index-4t0r3t3b.js +0 -2
- package/dist/chunk-index-6twb7ccd.js +0 -2
- package/dist/chunk-index-7xffskfa.js +0 -8
- package/dist/chunk-index-aqw7cy5j.js +0 -1
- package/dist/chunk-index-b6ca02e8.js +0 -2
- package/dist/chunk-index-b8nvhcx5.js +0 -2
- package/dist/chunk-index-gr6e0hse.js +0 -2
- package/dist/chunk-index-j214tczc.js +0 -1
- package/dist/chunk-index-jd6632ef.js +0 -2
- package/dist/chunk-index-jw7dtshr.js +0 -2
- package/dist/chunk-index-mmg788a1.js +0 -2
- package/dist/chunk-index-mq0dacwr.js +0 -2
- package/dist/chunk-index-p53ak25v.js +0 -2
- package/dist/chunk-index-rbcz9jd4.css +0 -1
- package/dist/chunk-index-rnxj3z8k.js +0 -2
- package/dist/chunk-index-v3bmr0nw.js +0 -2
- package/dist/chunk-index-wrm8w165.js +0 -2
- package/dist/chunk-index-xh0mg9cw.js +0 -2
- package/dist/chunk-index-ybtvvx85.js +0 -2
- package/dist/chunk-index-zynq0b0w.js +0 -50
- package/dist/chunk-lang-css-88v3hp3d.js +0 -1
- package/dist/chunk-lang-html-07d47es4.js +0 -1
- package/dist/chunk-lang-javascript-fmhhgzwd.js +0 -1
- package/dist/chunk-lang-typescript-etjjvr2g.js +0 -1
- package/dist/chunk-layout-actions-48ecgtkg.js +0 -1
- package/dist/chunk-menu-items-1epy3h0t.js +0 -2
- package/dist/chunk-mode-toggle-fxmfxsa4.js +0 -1
- package/dist/chunk-notes-yb8f057c.js +0 -1
- package/dist/chunk-notice-bell-wr4f1rph.js +0 -1
- package/dist/chunk-notices-x07da5bb.js +0 -1
- package/dist/chunk-onboarding-csrkbh9z.js +0 -1
- package/dist/chunk-overview-avc2d30t.js +0 -1
- package/dist/chunk-palette-9nhp95bz.js +0 -1
- package/dist/chunk-placement-items-q694dpgk.js +0 -1
- package/dist/chunk-profile-6qemf3m7.js +0 -1
- package/dist/chunk-prompt-history-7enzp2p3.js +0 -1
- package/dist/chunk-prompts-nv6mrn0q.js +0 -1
- package/dist/chunk-room-confirms-zz7n61y7.js +0 -1
- package/dist/chunk-room-menu-rr6nzap1.js +0 -1
- package/dist/chunk-rooms-menu-button-zbtwj47d.js +0 -1
- package/dist/chunk-schedules-rw57nr0m.js +0 -1
- package/dist/chunk-sheet-hosts-jhvaacst.js +0 -1
- package/dist/chunk-skill-editor-sz8an8qr.js +0 -8
- package/dist/chunk-skills-tnr1h16h.js +0 -1
- package/dist/chunk-switcher-list-17enhrse.js +0 -1
- package/dist/chunk-table-r12cja6p.js +0 -1
- package/dist/chunk-table-seats-g3t392zz.js +0 -1
- package/dist/chunk-table2d-p3ezsdcj.js +0 -1
- package/dist/chunk-table3d-4x2n2b00.js +0 -4220
- package/dist/chunk-thread-actions-qc6ezkha.js +0 -1
- package/dist/chunk-transcript-n2s6dher.js +0 -1
- package/dist/chunk-where-f3scdydc.js +0 -1
- package/dist/chunk-workspace-xs023bxm.js +0 -1
- package/dist/chunk-zh-tw-efvmk3fd.js +0 -1
package/src/host/host-app.ts
CHANGED
|
@@ -24,7 +24,8 @@ import { installFrames } from "./frames.ts";
|
|
|
24
24
|
import type { HostContext } from "./host-context.ts";
|
|
25
25
|
import { hostArea, pagesArea } from "./host-routes.ts";
|
|
26
26
|
import { HostPagesService, PAGE_BINDING } from "./pages.ts";
|
|
27
|
-
import
|
|
27
|
+
import { readAll, unreadAll } from "./read-all.ts";
|
|
28
|
+
import type { MovedMark, RoomReads } from "./reads.ts";
|
|
28
29
|
import { type HostSweep, sweepHostKinds } from "./retention.ts";
|
|
29
30
|
import { HostRoomsService, ListedKinds } from "./rooms-service.ts";
|
|
30
31
|
|
|
@@ -171,6 +172,19 @@ export class HostApp {
|
|
|
171
172
|
return this.access.read(viewer, binding as ChannelKey);
|
|
172
173
|
}
|
|
173
174
|
|
|
175
|
+
/** Reads the named rooms the viewer may read up to their newest message; the cursors that moved. */
|
|
176
|
+
readAll(viewer: Viewer, rooms: readonly string[]): Promise<MovedMark[]> {
|
|
177
|
+
return readAll(this.#context, viewer, rooms);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/** Puts back cursors that `readAll` moved, where the viewer has not read on since; the rooms put back. */
|
|
181
|
+
unreadAll(
|
|
182
|
+
viewer: Viewer,
|
|
183
|
+
moves: readonly { room: string; from: number; to: number }[],
|
|
184
|
+
): Promise<string[]> {
|
|
185
|
+
return unreadAll(this.#context, viewer, moves);
|
|
186
|
+
}
|
|
187
|
+
|
|
174
188
|
/** Applies the kinds' retention; the counts of what it removed. */
|
|
175
189
|
sweep(): Promise<HostSweep> {
|
|
176
190
|
return sweepHostKinds(this.#context);
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import type { Viewer } from "../web-app.ts";
|
|
2
|
+
import { hostRoomOf, readableAudience } from "./frames.ts";
|
|
3
|
+
import type { HostContext } from "./host-context.ts";
|
|
4
|
+
import type { MovedMark } from "./reads.ts";
|
|
5
|
+
|
|
6
|
+
/** The most rooms one bulk read, or one undo of it, names. */
|
|
7
|
+
export const MAX_READ_ALL = 500;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Reads every one of `rooms` the viewer may read now up to its newest message, and tells the
|
|
11
|
+
* others: each room's readers see the new mark (as for a single `read` frame), and the viewer's own
|
|
12
|
+
* pages, wherever they follow the room or not, hear of it too. A room the viewer may not read, or
|
|
13
|
+
* that is none, is skipped as if it were not named. Gives the cursors that moved.
|
|
14
|
+
*/
|
|
15
|
+
export async function readAll(
|
|
16
|
+
ctx: HostContext,
|
|
17
|
+
viewer: Viewer,
|
|
18
|
+
rooms: readonly string[],
|
|
19
|
+
): Promise<MovedMark[]> {
|
|
20
|
+
const audiences = new Map<
|
|
21
|
+
string,
|
|
22
|
+
Awaited<ReturnType<typeof readableAudience>>
|
|
23
|
+
>();
|
|
24
|
+
await Promise.all(
|
|
25
|
+
[...new Set(rooms)].map(async (room) => {
|
|
26
|
+
audiences.set(room, await readableAudience(ctx, viewer, room));
|
|
27
|
+
}),
|
|
28
|
+
);
|
|
29
|
+
const allowed = [...audiences].flatMap(([room, audience]) =>
|
|
30
|
+
audience ? [room] : [],
|
|
31
|
+
);
|
|
32
|
+
const moved = await ctx.reads.advanceAll(viewer.principalId, allowed);
|
|
33
|
+
for (const mark of moved) {
|
|
34
|
+
const audience = audiences.get(mark.room);
|
|
35
|
+
if (!audience) continue;
|
|
36
|
+
const frame = {
|
|
37
|
+
type: "read",
|
|
38
|
+
room: mark.room,
|
|
39
|
+
principal: viewer.principalId,
|
|
40
|
+
name: viewer.name,
|
|
41
|
+
upTo: mark.to,
|
|
42
|
+
at: mark.at,
|
|
43
|
+
} as const;
|
|
44
|
+
ctx.sockets.deliver(frame, audience);
|
|
45
|
+
// A host room reaches only the pages that follow it; the viewer's other pages show its count in a list.
|
|
46
|
+
if ("room" in audience)
|
|
47
|
+
ctx.sockets.deliver(frame, { principal: viewer.principalId });
|
|
48
|
+
}
|
|
49
|
+
return moved;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Puts back cursors that `readAll` moved, for the rooms the viewer may still read and whose cursor
|
|
54
|
+
* is still where it left it. The viewer's pages read their lists again to find the counts back.
|
|
55
|
+
* Gives the rooms put back.
|
|
56
|
+
*/
|
|
57
|
+
export async function unreadAll(
|
|
58
|
+
ctx: HostContext,
|
|
59
|
+
viewer: Viewer,
|
|
60
|
+
moves: readonly { room: string; from: number; to: number }[],
|
|
61
|
+
): Promise<string[]> {
|
|
62
|
+
const allowed: { room: string; from: number; to: number }[] = [];
|
|
63
|
+
await Promise.all(
|
|
64
|
+
moves.map(async (move) => {
|
|
65
|
+
if (await readableAudience(ctx, viewer, move.room)) allowed.push(move);
|
|
66
|
+
}),
|
|
67
|
+
);
|
|
68
|
+
const restored = await ctx.reads.rewind(viewer.principalId, allowed);
|
|
69
|
+
const kinds = new Set<string>();
|
|
70
|
+
for (const room of restored) {
|
|
71
|
+
const kind = hostRoomOf(ctx, room)?.kind.kind;
|
|
72
|
+
if (kind) kinds.add(kind);
|
|
73
|
+
}
|
|
74
|
+
for (const kind of kinds)
|
|
75
|
+
ctx.sockets.deliver(
|
|
76
|
+
{ type: "list_changed", kind },
|
|
77
|
+
{ principal: viewer.principalId },
|
|
78
|
+
);
|
|
79
|
+
return restored;
|
|
80
|
+
}
|
package/src/host/reads.ts
CHANGED
|
@@ -10,6 +10,15 @@ export interface ReadMark {
|
|
|
10
10
|
at: string;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
+
/** A cursor a bulk read moved: where it was (0 when the person never read the room) and where it is now. */
|
|
14
|
+
export interface MovedMark {
|
|
15
|
+
room: string;
|
|
16
|
+
from: number;
|
|
17
|
+
to: number;
|
|
18
|
+
/** ISO time. */
|
|
19
|
+
at: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
13
22
|
interface MarkRow {
|
|
14
23
|
principal_id: string;
|
|
15
24
|
last_read_id: string | number | bigint;
|
|
@@ -64,6 +73,64 @@ export class RoomReads {
|
|
|
64
73
|
return row && markOf(row);
|
|
65
74
|
}
|
|
66
75
|
|
|
76
|
+
/**
|
|
77
|
+
* Moves the person's cursor in each of `rooms` up to the room's newest message. Rooms with
|
|
78
|
+
* nothing past the cursor, and rooms with no messages, are left out of the answer, which gives
|
|
79
|
+
* for every room moved where its cursor was and where it is.
|
|
80
|
+
*/
|
|
81
|
+
async advanceAll(
|
|
82
|
+
principalId: string,
|
|
83
|
+
rooms: readonly string[],
|
|
84
|
+
): Promise<MovedMark[]> {
|
|
85
|
+
if (rooms.length === 0) return [];
|
|
86
|
+
const rows: {
|
|
87
|
+
room: string;
|
|
88
|
+
last_read_id: string | number | bigint;
|
|
89
|
+
was: string | number | bigint | null;
|
|
90
|
+
read_at: Date;
|
|
91
|
+
}[] = await this.#sql`
|
|
92
|
+
WITH before AS (
|
|
93
|
+
SELECT room, last_read_id FROM web_room_reads
|
|
94
|
+
WHERE principal_id = ${principalId} AND room IN ${this.#sql(rooms)}
|
|
95
|
+
), newest AS (
|
|
96
|
+
SELECT room, max(id) AS id FROM web_room_messages
|
|
97
|
+
WHERE room IN ${this.#sql(rooms)} GROUP BY room
|
|
98
|
+
)
|
|
99
|
+
INSERT INTO web_room_reads (room, principal_id, last_read_id)
|
|
100
|
+
SELECT room, ${principalId}, id FROM newest
|
|
101
|
+
ON CONFLICT (room, principal_id) DO UPDATE
|
|
102
|
+
SET last_read_id = EXCLUDED.last_read_id, read_at = now()
|
|
103
|
+
WHERE web_room_reads.last_read_id < EXCLUDED.last_read_id
|
|
104
|
+
RETURNING room, last_read_id, read_at,
|
|
105
|
+
(SELECT b.last_read_id FROM before b WHERE b.room = web_room_reads.room) AS was`;
|
|
106
|
+
return rows.map((row) => ({
|
|
107
|
+
room: row.room,
|
|
108
|
+
from: Number(row.was ?? 0),
|
|
109
|
+
to: Number(row.last_read_id),
|
|
110
|
+
at: row.read_at.toISOString(),
|
|
111
|
+
}));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Puts back cursors that `advanceAll` moved: each room's goes from `to` back to `from`, unless
|
|
116
|
+
* the person read on since (their cursor is no longer at `to`). Gives the rooms put back.
|
|
117
|
+
*/
|
|
118
|
+
async rewind(
|
|
119
|
+
principalId: string,
|
|
120
|
+
moves: readonly { room: string; from: number; to: number }[],
|
|
121
|
+
): Promise<string[]> {
|
|
122
|
+
if (moves.length === 0) return [];
|
|
123
|
+
const rows: { room: string }[] = await this.#sql`
|
|
124
|
+
UPDATE web_room_reads r SET last_read_id = v.was, read_at = now()
|
|
125
|
+
FROM jsonb_to_recordset(${JSON.stringify(
|
|
126
|
+
moves.map((move) => ({ room: move.room, was: move.from, at: move.to })),
|
|
127
|
+
)}::text::jsonb) AS v(room text, was bigint, at bigint)
|
|
128
|
+
WHERE r.principal_id = ${principalId} AND r.room = v.room AND r.last_read_id = v.at
|
|
129
|
+
AND v.was < v.at
|
|
130
|
+
RETURNING r.room`;
|
|
131
|
+
return rows.map((row) => row.room);
|
|
132
|
+
}
|
|
133
|
+
|
|
67
134
|
/** The person's cursor in the room, or undefined when they never read it. */
|
|
68
135
|
async cursorOf(
|
|
69
136
|
room: string,
|
package/src/i18n/zh-tw.ts
CHANGED
|
@@ -34,7 +34,7 @@ export const ZH_TW: Readonly<Record<string, string>> = {
|
|
|
34
34
|
"binary file": "二進位檔",
|
|
35
35
|
"too large": "檔案太大",
|
|
36
36
|
"not UTF-8": "不是 UTF-8",
|
|
37
|
-
"
|
|
37
|
+
"Knights' workspace": "騎士工作區",
|
|
38
38
|
Scratch: "暫存區",
|
|
39
39
|
Path: "路徑",
|
|
40
40
|
"This folder is empty": "這個資料夾是空的",
|
|
@@ -59,7 +59,7 @@ export const ZH_TW: Readonly<Record<string, string>> = {
|
|
|
59
59
|
"Read only: {reason}": "唯讀:{reason}",
|
|
60
60
|
"File menu": "檔案選單",
|
|
61
61
|
"This file is gone": "這個檔案已經不見了",
|
|
62
|
-
"A
|
|
62
|
+
"A knight is changing this file": "騎士正在改這個檔案",
|
|
63
63
|
"Reload (discard my edits)": "重新載入(放棄我的修改)",
|
|
64
64
|
"This file is too large to show here.": "這個檔案太大,無法在這裡顯示。",
|
|
65
65
|
"This file is not text, so it cannot be shown here.":
|
|
@@ -82,7 +82,7 @@ export const ZH_TW: Readonly<Record<string, string>> = {
|
|
|
82
82
|
"Copy my text": "複製我的內容",
|
|
83
83
|
"Save mine as a new file": "存成新檔案",
|
|
84
84
|
"Save mine anyway": "仍用我的版本覆蓋",
|
|
85
|
-
"Replace the
|
|
85
|
+
"Replace the knight's changes?": "要蓋掉騎士的修改嗎?",
|
|
86
86
|
"The file has been changed since you opened it. Saving yours writes over those changes.":
|
|
87
87
|
"你開啟後,這個檔案又被改過。存下你的版本會蓋掉那些修改。",
|
|
88
88
|
"Unsaved changes, leave?": "還沒存檔,要離開嗎?",
|
|
@@ -257,7 +257,6 @@ export const ZH_TW: Readonly<Record<string, string>> = {
|
|
|
257
257
|
"Yolo mode on": "Yolo 模式已開啟",
|
|
258
258
|
|
|
259
259
|
// 命令面板
|
|
260
|
-
Agent: "夥伴",
|
|
261
260
|
"Find a room or page": "尋找聊天室或頁面",
|
|
262
261
|
"Nothing matches.": "沒有符合的項目。",
|
|
263
262
|
Page: "頁面",
|
|
@@ -280,19 +279,19 @@ export const ZH_TW: Readonly<Record<string, string>> = {
|
|
|
280
279
|
"讓回合可超過時間上限繼續進行,最多到主機設定的上限。提問仍會等你回應。",
|
|
281
280
|
"Long-running mode": "長時間執行模式",
|
|
282
281
|
"Name, prompt, model and picture": "名稱、提示詞、模型與圖像",
|
|
283
|
-
"Reply to the
|
|
282
|
+
"Reply to the knight that started this thread.": "回覆開啟這個討論串的騎士。",
|
|
284
283
|
"Yolo mode": "Yolo 模式",
|
|
285
|
-
"No
|
|
284
|
+
"No knights are seated yet.": "圓桌上還沒有騎士入座。",
|
|
286
285
|
"Who's at the table": "誰上桌",
|
|
287
286
|
"Off the table": "離席中",
|
|
288
287
|
"Leave the table": "離席",
|
|
289
288
|
"{name} left the table": "{name}已離席",
|
|
290
289
|
"The table is empty": "圓桌空了",
|
|
291
290
|
"There are no rooms to seat yet.": "還沒有可以入座的聊天室。",
|
|
292
|
-
"Every
|
|
293
|
-
"
|
|
294
|
-
"
|
|
295
|
-
"
|
|
291
|
+
"Every knight has left the table. Choose who sits down.":
|
|
292
|
+
"所有騎士都離席了。選擇誰要上桌。",
|
|
293
|
+
"A knight who leaves the table is muted: its room stops pushing to you. A card that waits for you is always pushed.":
|
|
294
|
+
"離席的騎士會被靜音:它的聊天室不再推播給你。等你回應的卡片仍會推播。",
|
|
296
295
|
|
|
297
296
|
// 通知
|
|
298
297
|
"A file is no longer available. Attach it again.":
|
|
@@ -328,11 +327,13 @@ export const ZH_TW: Readonly<Record<string, string>> = {
|
|
|
328
327
|
Today: "今天",
|
|
329
328
|
Yesterday: "昨天",
|
|
330
329
|
|
|
331
|
-
//
|
|
330
|
+
// 騎士
|
|
332
331
|
"Class, colors and parts. The picture here, in the rooms and in Discord all follow it.":
|
|
333
332
|
"職業、配色與部件。這裡、聊天室與 Discord 的圖像都會跟著改變。",
|
|
334
|
-
|
|
335
|
-
"
|
|
333
|
+
Default: "預設",
|
|
334
|
+
"Default model": "預設模型",
|
|
335
|
+
"Default model ({current})": "預設模型({current})",
|
|
336
|
+
"Default model (server setting)": "預設模型(跟著伺服器設定)",
|
|
336
337
|
Look: "造型",
|
|
337
338
|
Name: "名稱",
|
|
338
339
|
Prompt: "提示詞",
|
|
@@ -350,8 +351,8 @@ export const ZH_TW: Readonly<Record<string, string>> = {
|
|
|
350
351
|
"Tool list": "工具清單",
|
|
351
352
|
Unreachable: "無法連線",
|
|
352
353
|
"Upstream gateways": "上游閘道",
|
|
353
|
-
"Upstream MCP gateways and virtual servers used by the
|
|
354
|
-
"
|
|
354
|
+
"Upstream MCP gateways and virtual servers used by the knights.":
|
|
355
|
+
"騎士使用的上游 MCP 閘道與虛擬伺服器。",
|
|
355
356
|
"Used by": "使用者",
|
|
356
357
|
"Virtual servers": "虛擬伺服器",
|
|
357
358
|
|
|
@@ -361,15 +362,15 @@ export const ZH_TW: Readonly<Record<string, string>> = {
|
|
|
361
362
|
"None stored.": "沒有儲存的內容。",
|
|
362
363
|
running: "進行中",
|
|
363
364
|
"Owner channels": "擁有者頻道",
|
|
364
|
-
"Direct messages and channels where the owner talks to
|
|
365
|
-
"
|
|
366
|
-
"
|
|
367
|
-
"The
|
|
365
|
+
"Direct messages and channels where the owner talks to a knight.":
|
|
366
|
+
"擁有者與騎士交談的私訊與頻道。",
|
|
367
|
+
"Bot server channels": "Bot 伺服器頻道",
|
|
368
|
+
"The bot server's one-to-one channels.": "Bot 伺服器的一對一頻道。",
|
|
368
369
|
"Group channels": "群組頻道",
|
|
369
|
-
"The
|
|
370
|
-
"Outside
|
|
371
|
-
"Conversations an
|
|
372
|
-
"Discord
|
|
370
|
+
"The bot server's group channels.": "Bot 伺服器的群組頻道。",
|
|
371
|
+
"Outside AI clients": "外部 AI 用戶端",
|
|
372
|
+
"Conversations an outside AI client holds over MCP.":
|
|
373
|
+
"Discord 以外的 AI 用戶端透過 MCP 進行的對話。",
|
|
373
374
|
"Plugin conversations": "外掛對話",
|
|
374
375
|
"Conversations a plugin runs, such as a web chat, as the host recorded them.":
|
|
375
376
|
"外掛(例如網頁聊天)進行的對話,內容為主機所記錄。",
|
|
@@ -398,28 +399,28 @@ export const ZH_TW: Readonly<Record<string, string>> = {
|
|
|
398
399
|
"Search (words separated by spaces)": "搜尋(以空格分隔關鍵字)",
|
|
399
400
|
"Search notes": "搜尋記憶",
|
|
400
401
|
Text: "內容",
|
|
401
|
-
"What
|
|
402
|
-
"
|
|
402
|
+
"What a knight remembers. A change applies from the next turn.":
|
|
403
|
+
"騎士記得的內容。變更會從下一個回合起生效。",
|
|
403
404
|
"Whose notes": "誰的記憶",
|
|
404
405
|
Core: "核心",
|
|
405
406
|
"Carried into every turn.": "每個回合都會帶入。",
|
|
406
407
|
Note: "備忘",
|
|
407
|
-
"Searched when
|
|
408
|
+
"Searched when a knight needs them.": "騎士需要時才會搜尋。",
|
|
408
409
|
Events: "事件",
|
|
409
410
|
"Carried into every turn until their date.": "到期日之前每個回合都會帶入。",
|
|
410
411
|
|
|
411
412
|
// 總覽
|
|
412
413
|
"(No messages)": "(沒有訊息)",
|
|
413
414
|
"{count} waiting": "{count} 項等待中",
|
|
414
|
-
"
|
|
415
|
-
"
|
|
416
|
-
"
|
|
415
|
+
"Knight and group status; starting over archives the conversation and preserves memory.":
|
|
416
|
+
"騎士與群組的狀態;重新開始會封存對話並保留記憶。",
|
|
417
|
+
"Bot server": "Bot 伺服器",
|
|
417
418
|
Context: "上下文",
|
|
418
419
|
"Conversation {size} · Archives {count}": "對話 {size} · 封存 {count} 筆",
|
|
419
420
|
"Conversations opened through remote MCP; after deletion the session ID cannot be resumed.":
|
|
420
421
|
"透過遠端 MCP 開啟的對話;刪除後無法再用該工作階段 ID 接續。",
|
|
421
|
-
"Direct messages and channels where you mention
|
|
422
|
-
"
|
|
422
|
+
"Direct messages and channels where you mention a knight. Start over archives; delete removes every archive permanently.":
|
|
423
|
+
"你在私訊與頻道中提及騎士的對話。重新開始會封存;刪除會永久移除所有封存。",
|
|
423
424
|
"Enabled by {name} at {time}": "由{name}於 {time} 啟用",
|
|
424
425
|
idle: "閒置",
|
|
425
426
|
"Just compacted (limit {limit})": "剛完成壓縮(上限 {limit})",
|
|
@@ -427,13 +428,13 @@ export const ZH_TW: Readonly<Record<string, string>> = {
|
|
|
427
428
|
"Members: {members} | Host: {host}": "成員:{members}|主持:{host}",
|
|
428
429
|
Model: "模型",
|
|
429
430
|
"My workspaces": "我的工作區",
|
|
430
|
-
"No
|
|
431
|
+
"No knights.": "沒有騎士。",
|
|
431
432
|
"No live conversation · Archives {count}":
|
|
432
433
|
"沒有進行中的對話 · 封存 {count} 筆",
|
|
433
|
-
"No outside
|
|
434
|
+
"No outside AI client conversations.": "沒有外部 AI 用戶端的對話。",
|
|
434
435
|
"No party channels.": "沒有派對頻道。",
|
|
435
436
|
"No workspaces.": "沒有工作區。",
|
|
436
|
-
"Outside
|
|
437
|
+
"Outside AI client conversations": "外部 AI 用戶端的對話",
|
|
437
438
|
"Party channels": "派對頻道",
|
|
438
439
|
"Replying in {name}'s channel": "正在{name}的頻道中回覆",
|
|
439
440
|
"Replying in another channel": "正在其他頻道中回覆",
|
|
@@ -452,8 +453,8 @@ export const ZH_TW: Readonly<Record<string, string>> = {
|
|
|
452
453
|
"Back to rooms": "回到聊天室列表",
|
|
453
454
|
"Every approval it makes is written into the conversation, and a banner stays up while yolo mode is on.":
|
|
454
455
|
"它做的每一次核准都會寫進對話,而且 Yolo 模式開啟期間會一直顯示橫幅。",
|
|
455
|
-
"In this room, the
|
|
456
|
-
"
|
|
456
|
+
"In this room, the knight's held actions are approved for you, without a card, as soon as they come up.":
|
|
457
|
+
"在這個聊天室中,騎士保留的操作一出現就會替你核准,不會顯示卡片。",
|
|
457
458
|
Latest: "最新",
|
|
458
459
|
"Load earlier messages": "載入更早的訊息",
|
|
459
460
|
"Loading…": "載入中…",
|
|
@@ -479,7 +480,7 @@ export const ZH_TW: Readonly<Record<string, string>> = {
|
|
|
479
480
|
"Working…": "處理中…",
|
|
480
481
|
|
|
481
482
|
// 長按與右鍵選單
|
|
482
|
-
"Edit
|
|
483
|
+
"Edit knight": "編輯騎士",
|
|
483
484
|
"Change look": "換外觀",
|
|
484
485
|
"Notifications are off": "通知已關閉",
|
|
485
486
|
"Notifications are on for {name}": "已開啟{name}的通知",
|
|
@@ -503,27 +504,27 @@ export const ZH_TW: Readonly<Record<string, string>> = {
|
|
|
503
504
|
"Quote reply": "引用回覆",
|
|
504
505
|
"Select text": "選取文字",
|
|
505
506
|
Share: "分享",
|
|
506
|
-
"
|
|
507
|
+
"Knight profile": "騎士資料",
|
|
507
508
|
"Copy details": "複製細節",
|
|
508
509
|
Download: "下載",
|
|
509
510
|
"Could not copy.": "無法複製。",
|
|
510
511
|
"Could not share.": "無法分享。",
|
|
511
512
|
|
|
512
513
|
// 新增、封存與群組
|
|
513
|
-
"New
|
|
514
|
+
"New knight": "新增騎士",
|
|
514
515
|
"New group": "新增群組",
|
|
515
516
|
Archived: "已封存",
|
|
516
517
|
Archive: "封存",
|
|
517
518
|
"Archive {name}": "封存{name}",
|
|
518
519
|
"Archive {name}?": "要封存{name}嗎?",
|
|
519
|
-
"Archive
|
|
520
|
+
"Archive knight": "封存騎士",
|
|
520
521
|
"Archive group": "封存群組",
|
|
521
522
|
"Archived {name}.": "已封存{name}。",
|
|
522
523
|
"Restored {name}.": "已還原{name}。",
|
|
523
524
|
"Deleted {name}.": "已刪除{name}。",
|
|
524
525
|
"Delete {name}?": "要刪除{name}嗎?",
|
|
525
526
|
"Made {name}.": "已建立{name}。",
|
|
526
|
-
"Make
|
|
527
|
+
"Make knight": "建立騎士",
|
|
527
528
|
"Make group": "建立群組",
|
|
528
529
|
"Making…": "建立中…",
|
|
529
530
|
"Permanent name": "固定名稱",
|
|
@@ -532,8 +533,8 @@ export const ZH_TW: Readonly<Record<string, string>> = {
|
|
|
532
533
|
"Its job, its sources, its limits and how it reports.":
|
|
533
534
|
"它的工作、資料來源、限制,以及怎麼回報。",
|
|
534
535
|
"Opening task": "開場任務",
|
|
535
|
-
"Optional. The
|
|
536
|
-
"
|
|
536
|
+
"Optional. The knight starts on it as soon as it is made.":
|
|
537
|
+
"可不填。騎士建立後會立刻開始做。",
|
|
537
538
|
"Your look": "你選的外觀",
|
|
538
539
|
"Suggested from its role": "依角色建議的外觀",
|
|
539
540
|
"Choose a look": "選擇外觀",
|
|
@@ -541,48 +542,77 @@ export const ZH_TW: Readonly<Record<string, string>> = {
|
|
|
541
542
|
"Edit group": "編輯群組",
|
|
542
543
|
"Name, members and host": "名稱、成員與主持人",
|
|
543
544
|
Members: "成員",
|
|
544
|
-
"Pick 2 to 6
|
|
545
|
-
"選 2 到 6
|
|
545
|
+
"Pick 2 to 6 knights. They take turns in the order picked.":
|
|
546
|
+
"選 2 到 6 位騎士。他們依選取的順序輪流發言。",
|
|
546
547
|
Host: "主持人",
|
|
547
548
|
"It stops answering. The history is kept and it can be restored.":
|
|
548
549
|
"它會停止回應。歷史會保留,之後可以還原。",
|
|
549
|
-
"An archived
|
|
550
|
-
"
|
|
550
|
+
"An archived knight stops answering and its schedules are cancelled. Its history is kept, and you can restore it from Archived.":
|
|
551
|
+
"封存後的騎士不再回應,排程也會取消。歷史會保留,之後可以從「已封存」還原。",
|
|
551
552
|
"{name} stops answering and its schedules are cancelled. A group loses it as a member. The history is kept, and Archived in the room list restores it.":
|
|
552
553
|
"{name}會停止回應,排程也會取消;群組會少掉這位成員。歷史會保留,可以從聊天室列表的「已封存」還原。",
|
|
553
|
-
"Archived
|
|
554
|
-
"
|
|
554
|
+
"Archived knights and groups no longer answer. Their history is kept until you delete them.":
|
|
555
|
+
"已封存的騎士與群組不再回應。歷史會保留,直到你刪除它們。",
|
|
555
556
|
"Nothing is archived.": "沒有已封存的項目。",
|
|
556
557
|
"{name} and its messages in this app are deleted for good, and the name can be used again. This cannot be undone.":
|
|
557
558
|
"{name}和它在這個應用程式裡的訊息會永久刪除,名稱可以再使用。此動作無法復原。",
|
|
558
|
-
"What the
|
|
559
|
-
"
|
|
559
|
+
"What the knight can load when a task calls for it. It applies from its next turn.":
|
|
560
|
+
"騎士在任務需要時可以載入的技能。下一輪對話起生效。",
|
|
560
561
|
"Its file cannot be read.": "讀不到它的檔案。",
|
|
561
|
-
"Built in: every
|
|
562
|
+
"Built in: every knight carries it.": "內建:每位騎士都有。",
|
|
562
563
|
"This host has no skills.": "這個主機沒有技能。",
|
|
563
564
|
"Give add or remove.": "請給 add 或 remove。",
|
|
564
565
|
"members must be a list of text.": "members 必須是文字清單。",
|
|
565
566
|
"Give displayName, members or host.": "請給 displayName、members 或 host。",
|
|
566
567
|
|
|
567
568
|
// 聊天室列表
|
|
568
|
-
|
|
569
|
+
Knights: "騎士",
|
|
569
570
|
"Dispatch threads": "派工討論串",
|
|
570
571
|
"Find a room": "尋找聊天室",
|
|
571
572
|
"Switch rooms": "切換聊天室",
|
|
573
|
+
"Keyboard shortcuts": "鍵盤快速鍵",
|
|
574
|
+
"Previous room": "上一個聊天室",
|
|
575
|
+
"Next room": "下一個聊天室",
|
|
576
|
+
"Previous room with unread messages": "上一個有未讀訊息的聊天室",
|
|
577
|
+
"Next room with unread messages": "下一個有未讀訊息的聊天室",
|
|
578
|
+
"Close the open menu or sheet, or leave the message box":
|
|
579
|
+
"關閉開啟的選單或面板;都沒有時離開訊息輸入框",
|
|
580
|
+
"Show these shortcuts": "顯示這份快速鍵清單",
|
|
581
|
+
"In a message box the arrow shortcuts switch rooms only when it is empty or the caret is at its very start (up) or very end (down).":
|
|
582
|
+
"在訊息輸入框裡,方向鍵快速鍵只在框內沒有文字,或游標在最開頭(上)、最結尾(下)時才會切換聊天室。",
|
|
572
583
|
Group: "群組",
|
|
573
584
|
Groups: "群組",
|
|
574
585
|
Home: "首頁",
|
|
575
586
|
"Long-running": "長時間執行",
|
|
576
587
|
"No messages yet": "還沒有訊息",
|
|
577
|
-
"No rooms yet. Rooms appear when the team has
|
|
578
|
-
"
|
|
588
|
+
"No rooms yet. Rooms appear when the team has a knight.":
|
|
589
|
+
"還沒有聊天室。團隊有騎士後,聊天室就會出現。",
|
|
579
590
|
Yolo: "Yolo",
|
|
580
591
|
"{count} card waiting": "{count} 張卡片等待中",
|
|
581
592
|
"{count} cards waiting": "{count} 張卡片等待中",
|
|
582
593
|
"{count} finished thread": "{count} 個已結束的討論串",
|
|
583
594
|
"{count} finished threads": "{count} 個已結束的討論串",
|
|
584
|
-
|
|
595
|
+
Clear: "清除",
|
|
596
|
+
"Clear this thread": "清除這則",
|
|
597
|
+
"Clear all finished": "清除所有已完成",
|
|
598
|
+
"Clear finished": "清除已完成",
|
|
599
|
+
Finished: "已完成",
|
|
600
|
+
"In progress": "進行中",
|
|
601
|
+
"More actions for {name}": "{name} 的更多動作",
|
|
602
|
+
"Cleared “{name}”": "已清除「{name}」",
|
|
603
|
+
"Cleared {count} finished thread": "已清除 {count} 個已完成的討論串",
|
|
604
|
+
"Cleared {count} finished threads": "已清除 {count} 個已完成的討論串",
|
|
585
605
|
"The thread is still open.": "這個討論串還在進行中。",
|
|
606
|
+
"threads must be a list of at most 100 keys.":
|
|
607
|
+
"threads 必須是最多 100 個鍵的清單。",
|
|
608
|
+
"rooms must be a list of at most 500 keys.":
|
|
609
|
+
"rooms 必須是最多 500 個鍵的清單。",
|
|
610
|
+
"restore must be a list of at most 500 moves.":
|
|
611
|
+
"restore 必須是最多 500 筆移動紀錄的清單。",
|
|
612
|
+
"Mark all as read": "全部標為已讀",
|
|
613
|
+
"Marked {count} room as read": "已將 {count} 個聊天室標為已讀",
|
|
614
|
+
"Marked {count} rooms as read": "已將 {count} 個聊天室標為已讀",
|
|
615
|
+
"Clear all finished threads": "清除所有已結束的討論串",
|
|
586
616
|
|
|
587
617
|
// 排程
|
|
588
618
|
"{days} at {time}": "每{days} {time}",
|
|
@@ -594,8 +624,8 @@ export const ZH_TW: Readonly<Record<string, string>> = {
|
|
|
594
624
|
"Every {count} days at {time}": "每 {count} 天 {time}",
|
|
595
625
|
"Every day at {time}": "每天 {time}",
|
|
596
626
|
"next {when}": "下次:{when}",
|
|
597
|
-
"Nothing is scheduled. Ask
|
|
598
|
-
"
|
|
627
|
+
"Nothing is scheduled. Ask a knight to remind you or to run something later.":
|
|
628
|
+
"目前沒有排程。你可以請騎士提醒你,或稍後執行某件事。",
|
|
599
629
|
"Once on {date} at {time}": "{date} {time}(僅一次)",
|
|
600
630
|
"Schedule cancelled.": "排程已取消。",
|
|
601
631
|
"set by {name}": "由{name}設定",
|
|
@@ -613,8 +643,8 @@ export const ZH_TW: Readonly<Record<string, string>> = {
|
|
|
613
643
|
Appearance: "外觀",
|
|
614
644
|
"Color scheme": "色彩配置",
|
|
615
645
|
Device: "裝置",
|
|
616
|
-
"Get a notification on this device when a card waits for you or
|
|
617
|
-
"
|
|
646
|
+
"Get a notification on this device when a card waits for you or a knight reports on its own, while the app is not open.":
|
|
647
|
+
"應用程式未開啟時,只要有卡片等你處理,或騎士主動回報,這台裝置就會收到通知。",
|
|
618
648
|
"Install it to open from your home screen or dock, in its own window.":
|
|
619
649
|
"安裝後可從主畫面或 Dock 直接開啟,並使用獨立的視窗。",
|
|
620
650
|
"Install the app": "安裝應用程式",
|
|
@@ -625,8 +655,8 @@ export const ZH_TW: Readonly<Record<string, string>> = {
|
|
|
625
655
|
"Notifications were not allowed. Allow them in the browser's site settings.":
|
|
626
656
|
"通知未獲允許。請在瀏覽器的網站設定中允許通知。",
|
|
627
657
|
Notifications: "通知",
|
|
628
|
-
"Quiet hours, {start} to {end} ({zone}): no notification for
|
|
629
|
-
"勿擾時段 {start} 至 {end}({zone}
|
|
658
|
+
"Quiet hours, {start} to {end} ({zone}): no notification for a knight's report or a notice. A card waiting for you still notifies. The server sets the hours.":
|
|
659
|
+
"勿擾時段 {start} 至 {end}({zone}):這段時間內,騎士的回報與通知不會推播;等你處理的卡片仍會推播。時段由伺服器設定。",
|
|
630
660
|
Remove: "移除",
|
|
631
661
|
"Signed in as {name}.": "目前登入身分:{name}。",
|
|
632
662
|
"This browser cannot get push notifications. On iPhone and iPad, install the app from Share, Add to Home Screen, and open it from there.":
|
|
@@ -647,15 +677,15 @@ export const ZH_TW: Readonly<Record<string, string>> = {
|
|
|
647
677
|
"白天是羊皮紙,夜晚是夜間城堡,搭配金色分隔線、盾形頭像與蠟封印章。",
|
|
648
678
|
|
|
649
679
|
// 技能
|
|
650
|
-
"All
|
|
651
|
-
"
|
|
680
|
+
"All knights": "所有騎士",
|
|
681
|
+
"Knight-written": "騎士撰寫",
|
|
652
682
|
"Built-in": "內建",
|
|
653
683
|
Carriers: "搭載對象",
|
|
654
684
|
"File missing": "檔案遺失",
|
|
655
685
|
"No skills.": "沒有技能。",
|
|
656
686
|
None: "無",
|
|
657
|
-
"Registered skills and their carriers; ask
|
|
658
|
-
"已登錄的技能與搭載對象;如要修改內容,請在 Discord
|
|
687
|
+
"Registered skills and their carriers; ask a knight on Discord to edit their contents.":
|
|
688
|
+
"已登錄的技能與搭載對象;如要修改內容,請在 Discord 上請騎士編輯。",
|
|
659
689
|
|
|
660
690
|
// 圓桌
|
|
661
691
|
"Reset view": "重設視角",
|
|
@@ -671,18 +701,19 @@ export const ZH_TW: Readonly<Record<string, string>> = {
|
|
|
671
701
|
"Archived {name}": "已封存的{name}",
|
|
672
702
|
"Current conversation": "目前的對話",
|
|
673
703
|
"Nothing to show.": "沒有可顯示的內容。",
|
|
674
|
-
"Outside
|
|
704
|
+
"Outside AI client · {id}": "外部 AI 用戶端 · {id}",
|
|
675
705
|
Showing: "顯示",
|
|
676
706
|
"This conversation is longer than the console reads; the oldest entries are not shown.":
|
|
677
707
|
"這段對話比主控台能讀取的長度還長,最舊的紀錄不會顯示。",
|
|
678
708
|
"Tool failed": "工具執行失敗",
|
|
679
709
|
"Tool result": "工具結果",
|
|
680
710
|
User: "使用者",
|
|
681
|
-
|
|
711
|
+
// 命令面板與對話紀錄共用
|
|
712
|
+
Knight: "騎士",
|
|
682
713
|
"Summary of earlier conversation": "先前對話的摘要",
|
|
683
714
|
|
|
684
715
|
// 伺服器回應
|
|
685
|
-
"There is no such room,
|
|
716
|
+
"There is no such room, knight or file.": "找不到這個聊天室、騎士或檔案。",
|
|
686
717
|
"The body is too large.": "內容太大。",
|
|
687
718
|
"The body is not JSON.": "內容不是 JSON。",
|
|
688
719
|
"The body is not an object.": "內容不是物件。",
|
|
@@ -846,7 +877,7 @@ export const ZH_TW: Readonly<Record<string, string>> = {
|
|
|
846
877
|
"character.class.paladin": "聖騎士",
|
|
847
878
|
"character.class.archivist": "檔案管理員",
|
|
848
879
|
// 職業適合的工作
|
|
849
|
-
"character.jobs.wizard": "
|
|
880
|
+
"character.jobs.wizard": "協調者、規劃者、主騎士",
|
|
850
881
|
"character.jobs.knight": "審查者、品質把關、守護者",
|
|
851
882
|
"character.jobs.scholar": "研究員、文獻回顧",
|
|
852
883
|
"character.jobs.smith": "程式開發、建置、重構",
|
|
@@ -968,14 +999,14 @@ export const ZH_TW: Readonly<Record<string, string>> = {
|
|
|
968
999
|
// 主要提示詞與技能內容的編輯
|
|
969
1000
|
"Main prompts": "主要提示詞",
|
|
970
1001
|
"Edit the main prompts": "編輯主要提示詞",
|
|
971
|
-
"The texts the
|
|
972
|
-
"
|
|
973
|
-
"The texts the
|
|
974
|
-
"
|
|
1002
|
+
"The texts the main knight and the other knights start from, with a diff to read before you save and a way back to the file's text.":
|
|
1003
|
+
"主騎士與其他騎士開始工作時所依據的文字;儲存前可檢視差異,也能回到檔案裡的版本。",
|
|
1004
|
+
"The texts the main knight and the other knights start from. A text you save here replaces the one from the file from the next turn on, until you go back to the file's.":
|
|
1005
|
+
"主騎士與其他騎士開始工作時所依據的文字。你在這裡儲存的文字,從下一回合起取代檔案裡的版本,直到你回到檔案版本為止。",
|
|
975
1006
|
"Shared prompt": "共用提示詞",
|
|
976
1007
|
"Guest prompt": "訪客提示詞",
|
|
977
|
-
"The base every
|
|
978
|
-
"
|
|
1008
|
+
"The base every knight's prompt starts with, the main knight's included.":
|
|
1009
|
+
"每位騎士的提示詞都以它開頭,主騎士也不例外。",
|
|
979
1010
|
"Used instead of the shared prompt when the person speaking is not the owner.":
|
|
980
1011
|
"說話的人不是擁有者時,改用它取代共用提示詞。",
|
|
981
1012
|
"No prompts to edit.": "沒有可編輯的提示詞。",
|
|
@@ -1006,8 +1037,8 @@ export const ZH_TW: Readonly<Record<string, string>> = {
|
|
|
1006
1037
|
"這個技能在你編輯時被更動了。下方的差異是對照目前的版本;再儲存一次會取代它,或回去繼續編輯。",
|
|
1007
1038
|
"Frontmatter holds the name, which cannot change, and a description that starts with “Use when”. The rest is the skill.":
|
|
1008
1039
|
"Frontmatter 含有名稱(不能更改)與以「Use when」開頭的描述,其餘就是技能內容。",
|
|
1009
|
-
"Registered skills and their carriers. A skill
|
|
1010
|
-
"
|
|
1040
|
+
"Registered skills and their carriers. A skill a knight wrote can be edited here; the others are read-only.":
|
|
1041
|
+
"已登錄的技能與攜帶者。騎士撰寫的技能可以在這裡編輯,其他的唯讀。",
|
|
1011
1042
|
"Built in with the release; it changes with the release.":
|
|
1012
1043
|
"隨版本內建;要隨版本更新才會改變。",
|
|
1013
1044
|
"From the package {package}; it changes when the package is upgraded.":
|
|
@@ -1054,7 +1085,7 @@ export const ZH_TW: Readonly<Record<string, string>> = {
|
|
|
1054
1085
|
"You can read this room but not write in it.":
|
|
1055
1086
|
"你可以閱讀這個聊天室,但不能在其中發言。",
|
|
1056
1087
|
"@AI": "@AI",
|
|
1057
|
-
"The message starts with @AI, so the
|
|
1088
|
+
"The message starts with @AI, so the knight answers.":
|
|
1058
1089
|
"訊息開頭是 @AI,所以 AI 會回覆。",
|
|
1059
1090
|
"Write an internal note…": "寫一則內部備註…",
|
|
1060
1091
|
"Ask AI…": "問 AI…",
|
package/src/options.ts
CHANGED