@kubitnodes/stellaris 0.3.19 → 0.3.20
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/dist/cli/index.js +213 -105
- package/dist/server/index.js +201 -63
- package/package.json +1 -1
- package/web/dist/assets/BackLink-BjfX2KZl.js +1 -0
- package/web/dist/assets/{ChannelForms-BjD5Lp_u.js → ChannelForms-BYrcUldk.js} +1 -1
- package/web/dist/assets/ChannelView-GJ_DusLQ.js +2 -0
- package/web/dist/assets/{CitizenView-tvC6dNUB.js → CitizenView-Cx7IYSRr.js} +2 -2
- package/web/dist/assets/{CitizensView-DsUhIYtl.js → CitizensView-IKH7_yNQ.js} +1 -1
- package/web/dist/assets/{CliIcon-TlyIqh7V.js → CliIcon-vSuwLv2K.js} +1 -1
- package/web/dist/assets/CronsView-DJbi8Qhx.js +1 -0
- package/web/dist/assets/KnowledgeView-CoYOG9tI.js +1 -0
- package/web/dist/assets/{Markdown-CePsfcUS.js → Markdown-YC-fbHiE.js} +4 -4
- package/web/dist/assets/MessageItem-D7MUCA_T.js +1 -0
- package/web/dist/assets/MetricsView-CE94VW0I.js +1 -0
- package/web/dist/assets/NeedsYouView-Otnm3lcG.js +1 -0
- package/web/dist/assets/Overview-B8f6rLb2.js +1 -0
- package/web/dist/assets/{Pane-DGtHsANK.js → Pane-_D7n5uKH.js} +1 -1
- package/web/dist/assets/{Picker-Dq5Vvxod.js → Picker-DUiiQMFN.js} +1 -1
- package/web/dist/assets/ProjectView-D_ACOmEd.js +1 -0
- package/web/dist/assets/ProposalView-4d0U3nEb.js +2 -0
- package/web/dist/assets/{ProposalsView-BjEfHBtx.js → ProposalsView-BovF6CMD.js} +1 -1
- package/web/dist/assets/{RunnersView-CM-q_AX9.js → RunnersView-B2_uCVHC.js} +1 -1
- package/web/dist/assets/SocietyView-Bf0iX8AY.js +1 -0
- package/web/dist/assets/SubjectThread-B_Vmgs3d.js +1 -0
- package/web/dist/assets/{TaskFileView-BMAkbh-e.js → TaskFileView-BzO0gjE6.js} +2 -2
- package/web/dist/assets/{TaskView-HDwqXuYy.js → TaskView-DB_Nji99.js} +2 -2
- package/web/dist/assets/{TasksView-Dt8M_z96.js → TasksView-Bz36YYWq.js} +1 -1
- package/web/dist/assets/{ThreadForms-DbV3kPCi.js → ThreadForms-1mbMZsbv.js} +1 -1
- package/web/dist/assets/ThreadItem-QJSA92C4.js +1 -0
- package/web/dist/assets/ThreadView-BECLqt5B.js +1 -0
- package/web/dist/assets/{citizen-DQ0Bmk-r.js → citizen-DVFr5T6R.js} +1 -1
- package/web/dist/assets/{compose-Cx9c9k90.js → compose-_6feqP1t.js} +1 -1
- package/web/dist/assets/{format-SP8GnzAX.js → format-DxXtbdNM.js} +1 -1
- package/web/dist/assets/{governance-DX2ph-qr.js → governance-C9xH4QTm.js} +1 -1
- package/web/dist/assets/index-BEVCPm4C.js +23 -0
- package/web/dist/assets/index-BkOq4PSU.css +2 -0
- package/web/dist/assets/{link-DxSeFzFf.js → link-CTjFMNoY.js} +1 -1
- package/web/dist/assets/{tasks-BeWF0Xy2.js → tasks-B6I9DJ8O.js} +1 -1
- package/web/dist/assets/useMatch-B3RzAe8y.js +1 -0
- package/web/dist/assets/useStickyScroll-Ca7sWbGH.js +1 -0
- package/web/dist/index.html +9 -9
- package/web/dist/assets/BackLink-CDrsO9Ls.js +0 -1
- package/web/dist/assets/ChannelView-dI5EHpof.js +0 -2
- package/web/dist/assets/CronsView-BPlSyoAe.js +0 -1
- package/web/dist/assets/KnowledgeView-CgRGZrOK.js +0 -1
- package/web/dist/assets/MessageItem-kg5zWMDe.js +0 -1
- package/web/dist/assets/MetricsView-cJeDTfBE.js +0 -1
- package/web/dist/assets/NeedsYouView-Cd1TnY7W.js +0 -1
- package/web/dist/assets/Overview-B6aBmove.js +0 -1
- package/web/dist/assets/ProjectView-uskZHP8l.js +0 -1
- package/web/dist/assets/ProposalView-DmjIL53_.js +0 -2
- package/web/dist/assets/SocietyView-DZuGEqIl.js +0 -1
- package/web/dist/assets/SubjectThread-pCRDn42X.js +0 -1
- package/web/dist/assets/ThreadItem-BVlz2w6q.js +0 -1
- package/web/dist/assets/ThreadView-CpBytSH7.js +0 -1
- package/web/dist/assets/index-BDKI0Rje.js +0 -23
- package/web/dist/assets/index-BPVeX6tV.css +0 -2
- package/web/dist/assets/useMatch-HNekgJrF.js +0 -1
- package/web/dist/assets/useStickyScroll-BmqddNx5.js +0 -1
package/dist/server/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import path9 from "node:path";
|
|
|
6
6
|
import { serve } from "@hono/node-server";
|
|
7
7
|
|
|
8
8
|
// packages/board-core/dist/board.js
|
|
9
|
-
import { chmod as chmod2, cp, mkdir as mkdir4, readdir as readdir3, readFile as
|
|
9
|
+
import { chmod as chmod2, cp, mkdir as mkdir4, readdir as readdir3, readFile as readFile4, rename as rename3, rm as rm2, stat as stat3, writeFile as writeFile4 } from "node:fs/promises";
|
|
10
10
|
import path6 from "node:path";
|
|
11
11
|
import { decodeTime, monotonicFactory } from "ulid";
|
|
12
12
|
import { z as z13 } from "zod";
|
|
@@ -437,6 +437,14 @@ var MessageFrontmatterSchema = z4.object({
|
|
|
437
437
|
ts: IsoDateTimeSchema,
|
|
438
438
|
mentions: z4.array(NameSchema)
|
|
439
439
|
});
|
|
440
|
+
var MessageRefSchema = MessageFrontmatterSchema.pick({
|
|
441
|
+
id: true,
|
|
442
|
+
author: true,
|
|
443
|
+
channel: true,
|
|
444
|
+
thread: true,
|
|
445
|
+
ts: true
|
|
446
|
+
});
|
|
447
|
+
var MESSAGE_LOOKUP_LIMIT = 50;
|
|
440
448
|
var TaskStatusSchema = z4.enum(["open", "claimed", "done", "abandoned"]);
|
|
441
449
|
var ThreadStateSchema = z4.enum(["open", "closed"]);
|
|
442
450
|
var ThreadSubjectSchema = z4.object({
|
|
@@ -1861,7 +1869,7 @@ var RunnerConfigSchema = z11.object({
|
|
|
1861
1869
|
});
|
|
1862
1870
|
|
|
1863
1871
|
// packages/shared/dist/version.js
|
|
1864
|
-
var STELLARIS_VERSION = "0.3.
|
|
1872
|
+
var STELLARIS_VERSION = "0.3.20";
|
|
1865
1873
|
|
|
1866
1874
|
// packages/board-core/dist/errors.js
|
|
1867
1875
|
var BoardError = class extends Error {
|
|
@@ -1877,7 +1885,7 @@ function isBoardError(error, code) {
|
|
|
1877
1885
|
}
|
|
1878
1886
|
|
|
1879
1887
|
// packages/board-core/dist/events.js
|
|
1880
|
-
import { appendFile,
|
|
1888
|
+
import { appendFile, open, stat } from "node:fs/promises";
|
|
1881
1889
|
import path2 from "node:path";
|
|
1882
1890
|
|
|
1883
1891
|
// packages/board-core/dist/fs.js
|
|
@@ -1962,10 +1970,19 @@ async function listDirs(dir) {
|
|
|
1962
1970
|
|
|
1963
1971
|
// packages/board-core/dist/events.js
|
|
1964
1972
|
var LEADING_ID = /^\{"id":"([0-9A-HJKMNP-TV-Z]{26})"/;
|
|
1973
|
+
var ID_PREFIX_BYTES = 64;
|
|
1974
|
+
var NEWLINE = 10;
|
|
1965
1975
|
var EventLog = class {
|
|
1966
1976
|
file;
|
|
1967
1977
|
newId;
|
|
1968
1978
|
now;
|
|
1979
|
+
lines = [];
|
|
1980
|
+
/** Bytes the index covers: always the end of a complete line. */
|
|
1981
|
+
indexed = 0;
|
|
1982
|
+
/** The indexed file, to tell a replaced log from a grown one. */
|
|
1983
|
+
inode = null;
|
|
1984
|
+
scanning = Promise.resolve();
|
|
1985
|
+
listeners = /* @__PURE__ */ new Set();
|
|
1969
1986
|
constructor(file, newId, now) {
|
|
1970
1987
|
this.file = file;
|
|
1971
1988
|
this.newId = newId;
|
|
@@ -1982,48 +1999,97 @@ var EventLog = class {
|
|
|
1982
1999
|
await ensureDir(path2.dirname(this.file));
|
|
1983
2000
|
await appendFile(this.file, `${JSON.stringify(event)}
|
|
1984
2001
|
`, "utf8");
|
|
2002
|
+
for (const listener of this.listeners) {
|
|
2003
|
+
try {
|
|
2004
|
+
listener(event);
|
|
2005
|
+
} catch {
|
|
2006
|
+
}
|
|
2007
|
+
}
|
|
1985
2008
|
return event;
|
|
1986
2009
|
}
|
|
2010
|
+
/**
|
|
2011
|
+
* Calls `listener` after each event this process appends, until the returned function is
|
|
2012
|
+
* called. Another process's appends, such as the admin CLI's, are seen only by the next read.
|
|
2013
|
+
*/
|
|
2014
|
+
onAppend(listener) {
|
|
2015
|
+
this.listeners.add(listener);
|
|
2016
|
+
return () => {
|
|
2017
|
+
this.listeners.delete(listener);
|
|
2018
|
+
};
|
|
2019
|
+
}
|
|
1987
2020
|
/** Events with an id greater than `since`, oldest first. `null` reads from the beginning. */
|
|
1988
2021
|
async readSince(since, limit2 = 1e3) {
|
|
1989
|
-
|
|
1990
|
-
|
|
1991
|
-
|
|
1992
|
-
|
|
1993
|
-
const events = [];
|
|
1994
|
-
for (const line of raw.split("\n")) {
|
|
1995
|
-
if (line.trim().length === 0) {
|
|
1996
|
-
continue;
|
|
1997
|
-
}
|
|
1998
|
-
const leading = LEADING_ID.exec(line)?.[1];
|
|
1999
|
-
if (since !== null && leading !== void 0 && leading <= since) {
|
|
2022
|
+
await this.refresh();
|
|
2023
|
+
const chosen = [];
|
|
2024
|
+
for (const line of this.lines) {
|
|
2025
|
+
if (since !== null && line.id <= since) {
|
|
2000
2026
|
continue;
|
|
2001
2027
|
}
|
|
2002
|
-
|
|
2003
|
-
if (
|
|
2004
|
-
continue;
|
|
2005
|
-
}
|
|
2006
|
-
events.push(event);
|
|
2007
|
-
if (events.length >= limit2) {
|
|
2028
|
+
chosen.push(line);
|
|
2029
|
+
if (chosen.length >= limit2) {
|
|
2008
2030
|
break;
|
|
2009
2031
|
}
|
|
2010
2032
|
}
|
|
2011
|
-
|
|
2033
|
+
const first = chosen[0];
|
|
2034
|
+
const last = chosen.at(-1);
|
|
2035
|
+
if (first === void 0 || last === void 0) {
|
|
2036
|
+
return [];
|
|
2037
|
+
}
|
|
2038
|
+
const span = await this.readBytes(first.start, last.end);
|
|
2039
|
+
return chosen.map((line) => BoardEventSchema.parse(JSON.parse(span.toString("utf8", line.start - first.start, line.end - first.start))));
|
|
2012
2040
|
}
|
|
2013
2041
|
/** The id of the newest event, or null for an empty log. */
|
|
2014
2042
|
async lastId() {
|
|
2015
|
-
|
|
2016
|
-
|
|
2043
|
+
await this.refresh();
|
|
2044
|
+
return this.lines.at(-1)?.id ?? null;
|
|
2045
|
+
}
|
|
2046
|
+
/** Brings the index up to the file's end, one scan at a time. */
|
|
2047
|
+
refresh() {
|
|
2048
|
+
this.scanning = this.scanning.then(() => this.scan(), () => this.scan());
|
|
2049
|
+
return this.scanning;
|
|
2050
|
+
}
|
|
2051
|
+
async scan() {
|
|
2052
|
+
const info = await stat(this.file).catch((error) => {
|
|
2053
|
+
if (error instanceof Error && "code" in error && error.code === "ENOENT") {
|
|
2054
|
+
return null;
|
|
2055
|
+
}
|
|
2056
|
+
throw error;
|
|
2057
|
+
});
|
|
2058
|
+
if (info === null || info.ino !== this.inode || info.size < this.indexed) {
|
|
2059
|
+
this.lines = [];
|
|
2060
|
+
this.indexed = 0;
|
|
2061
|
+
this.inode = info?.ino ?? null;
|
|
2062
|
+
}
|
|
2063
|
+
if (info === null || info.size === this.indexed) {
|
|
2064
|
+
return;
|
|
2065
|
+
}
|
|
2066
|
+
const added = await this.readBytes(this.indexed, info.size);
|
|
2067
|
+
let start = 0;
|
|
2068
|
+
for (let at = added.indexOf(NEWLINE); at >= 0; at = added.indexOf(NEWLINE, start)) {
|
|
2069
|
+
const prefix = added.toString("utf8", start, Math.min(at, start + ID_PREFIX_BYTES));
|
|
2070
|
+
if (prefix.trim().length > 0) {
|
|
2071
|
+
const id = LEADING_ID.exec(prefix)?.[1] ?? BoardEventSchema.parse(JSON.parse(added.toString("utf8", start, at))).id;
|
|
2072
|
+
this.lines.push({ id, start: this.indexed + start, end: this.indexed + at });
|
|
2073
|
+
}
|
|
2074
|
+
start = at + 1;
|
|
2075
|
+
}
|
|
2076
|
+
this.indexed += start;
|
|
2077
|
+
}
|
|
2078
|
+
async readBytes(from, to) {
|
|
2079
|
+
const handle = await open(this.file, "r");
|
|
2080
|
+
try {
|
|
2081
|
+
const buffer = Buffer.alloc(to - from);
|
|
2082
|
+
const { bytesRead } = await handle.read(buffer, 0, buffer.length, from);
|
|
2083
|
+
return buffer.subarray(0, bytesRead);
|
|
2084
|
+
} finally {
|
|
2085
|
+
await handle.close();
|
|
2017
2086
|
}
|
|
2018
|
-
const lines = (await readFile2(this.file, "utf8")).trimEnd().split("\n");
|
|
2019
|
-
const last = lines.at(-1) ?? "";
|
|
2020
|
-
return last.length === 0 ? null : BoardEventSchema.parse(JSON.parse(last)).id;
|
|
2021
2087
|
}
|
|
2022
2088
|
};
|
|
2023
2089
|
|
|
2024
2090
|
// packages/board-core/dist/files.js
|
|
2025
2091
|
import { createHash } from "node:crypto";
|
|
2026
|
-
import { mkdir as mkdir2, readdir as readdir2, readFile as
|
|
2092
|
+
import { mkdir as mkdir2, readdir as readdir2, readFile as readFile2, rename as rename2, rm, stat as stat2, writeFile as writeFile2 } from "node:fs/promises";
|
|
2027
2093
|
import path3 from "node:path";
|
|
2028
2094
|
var MAX_FILE_BYTES = 8 * 1024 * 1024;
|
|
2029
2095
|
function isMissing2(error) {
|
|
@@ -2045,9 +2111,9 @@ var FileTree = class {
|
|
|
2045
2111
|
}
|
|
2046
2112
|
try {
|
|
2047
2113
|
const absolute = path3.join(root, ...relative.split("/"));
|
|
2048
|
-
const info = await
|
|
2114
|
+
const info = await stat2(absolute);
|
|
2049
2115
|
if (info.isFile() && info.size <= MAX_FILE_BYTES) {
|
|
2050
|
-
files[relative] = (await
|
|
2116
|
+
files[relative] = (await readFile2(absolute)).toString("base64");
|
|
2051
2117
|
}
|
|
2052
2118
|
} catch (error) {
|
|
2053
2119
|
if (!isMissing2(error)) {
|
|
@@ -2103,7 +2169,7 @@ var FileTree = class {
|
|
|
2103
2169
|
}
|
|
2104
2170
|
async hash(absolute) {
|
|
2105
2171
|
try {
|
|
2106
|
-
const info = await
|
|
2172
|
+
const info = await stat2(absolute);
|
|
2107
2173
|
if (info.size > MAX_FILE_BYTES) {
|
|
2108
2174
|
return null;
|
|
2109
2175
|
}
|
|
@@ -2112,7 +2178,7 @@ var FileTree = class {
|
|
|
2112
2178
|
if (known !== void 0 && known.mtimeMs === info.mtimeMs && known.size === info.size) {
|
|
2113
2179
|
return fileVersion(known.sha, executable);
|
|
2114
2180
|
}
|
|
2115
|
-
const sha = createHash("sha256").update(await
|
|
2181
|
+
const sha = createHash("sha256").update(await readFile2(absolute)).digest("hex");
|
|
2116
2182
|
this.hashes.set(absolute, { mtimeMs: info.mtimeMs, size: info.size, sha });
|
|
2117
2183
|
return fileVersion(sha, executable);
|
|
2118
2184
|
} catch (error) {
|
|
@@ -2125,7 +2191,7 @@ var FileTree = class {
|
|
|
2125
2191
|
};
|
|
2126
2192
|
|
|
2127
2193
|
// packages/board-core/dist/homes.js
|
|
2128
|
-
import { chmod, mkdir as mkdir3, readFile as
|
|
2194
|
+
import { chmod, mkdir as mkdir3, readFile as readFile3, writeFile as writeFile3 } from "node:fs/promises";
|
|
2129
2195
|
import path4 from "node:path";
|
|
2130
2196
|
import { execa } from "execa";
|
|
2131
2197
|
var HOME_GITIGNORE = [
|
|
@@ -2269,7 +2335,7 @@ var HomeRepos = class {
|
|
|
2269
2335
|
await writeFile3(ignore, IGNORE_FILE, "utf8");
|
|
2270
2336
|
await git(["add", "--all"], home);
|
|
2271
2337
|
await git(["commit", "--quiet", "--allow-empty", "-m", "home: created by the board"], home);
|
|
2272
|
-
} else if (await
|
|
2338
|
+
} else if (await readFile3(ignore, "utf8").catch(() => "") !== IGNORE_FILE) {
|
|
2273
2339
|
await writeFile3(ignore, IGNORE_FILE, "utf8");
|
|
2274
2340
|
await git(["add", ".gitignore"], home);
|
|
2275
2341
|
await git(["commit", "--quiet", "-m", "home: the board's ignore file"], home);
|
|
@@ -3188,8 +3254,8 @@ function restage(existing, edit) {
|
|
|
3188
3254
|
};
|
|
3189
3255
|
}
|
|
3190
3256
|
function reopenStage(stage) {
|
|
3191
|
-
const { completedBy: _by, completedAt: _at, ...
|
|
3192
|
-
return
|
|
3257
|
+
const { completedBy: _by, completedAt: _at, ...open2 } = stage;
|
|
3258
|
+
return open2;
|
|
3193
3259
|
}
|
|
3194
3260
|
function gateChanges(before, after) {
|
|
3195
3261
|
const changes = [];
|
|
@@ -3584,7 +3650,7 @@ var Board = class _Board {
|
|
|
3584
3650
|
const task = { ...TaskFrontmatterSchema.parse(raw.data), body: raw.body };
|
|
3585
3651
|
if ((legacy === "open" || legacy === "closed") && !await exists(this.paths.threadFile(threads, task.id))) {
|
|
3586
3652
|
const ended = task.status === "done" || task.status === "abandoned";
|
|
3587
|
-
const
|
|
3653
|
+
const open2 = legacy === "open" && !ended;
|
|
3588
3654
|
const openedBy = opened.get(task.id);
|
|
3589
3655
|
const closedBy = closed.get(task.id);
|
|
3590
3656
|
await this.writeThread(threads, {
|
|
@@ -3592,10 +3658,10 @@ var Board = class _Board {
|
|
|
3592
3658
|
channel: channelRef(slug, "general"),
|
|
3593
3659
|
title: task.title,
|
|
3594
3660
|
subject: { kind: "task", id: task.id },
|
|
3595
|
-
state:
|
|
3661
|
+
state: open2 ? "open" : "closed",
|
|
3596
3662
|
openedBy: openedBy?.actor ?? task.createdBy,
|
|
3597
3663
|
openedAt: openedBy?.ts ?? task.createdAt,
|
|
3598
|
-
...
|
|
3664
|
+
...open2 ? {} : {
|
|
3599
3665
|
closedBy: closedBy?.actor ?? SYSTEM_ACTOR.name,
|
|
3600
3666
|
closedAt: closedBy?.ts ?? this.now().toISOString()
|
|
3601
3667
|
},
|
|
@@ -3658,7 +3724,7 @@ var Board = class _Board {
|
|
|
3658
3724
|
for (const agent of await this.listAgents()) {
|
|
3659
3725
|
const file = this.paths.agentCursors(agent.name);
|
|
3660
3726
|
if (await exists(file)) {
|
|
3661
|
-
const legacy = Legacy.safeParse(JSON.parse(await
|
|
3727
|
+
const legacy = Legacy.safeParse(JSON.parse(await readFile4(file, "utf8")));
|
|
3662
3728
|
if (legacy.success) {
|
|
3663
3729
|
await writeJson(file, { digest: legacy.data.inbox });
|
|
3664
3730
|
}
|
|
@@ -3997,6 +4063,10 @@ ${charter.purpose}
|
|
|
3997
4063
|
async latestEventId() {
|
|
3998
4064
|
return this.events.lastId();
|
|
3999
4065
|
}
|
|
4066
|
+
/** Calls `listener` after each event the board appends, until the returned function is called. */
|
|
4067
|
+
onEventAppended(listener) {
|
|
4068
|
+
return this.events.onAppend(listener);
|
|
4069
|
+
}
|
|
4000
4070
|
/** The roster as projected: every citizen with identity, reach, availability, and profile. */
|
|
4001
4071
|
async listMembers() {
|
|
4002
4072
|
const members = [];
|
|
@@ -4041,7 +4111,7 @@ ${charter.purpose}
|
|
|
4041
4111
|
project,
|
|
4042
4112
|
revision: revision.success ? revision.data : 0,
|
|
4043
4113
|
updatedBy: updatedBy.success ? updatedBy.data : SYSTEM_ACTOR.name,
|
|
4044
|
-
updatedAt: updatedAt.success ? updatedAt.data : (await
|
|
4114
|
+
updatedAt: updatedAt.success ? updatedAt.data : (await stat3(file)).mtime.toISOString(),
|
|
4045
4115
|
body
|
|
4046
4116
|
};
|
|
4047
4117
|
}
|
|
@@ -4143,6 +4213,28 @@ ${charter.purpose}
|
|
|
4143
4213
|
async readThread(id) {
|
|
4144
4214
|
return (await this.findThread(id)).thread;
|
|
4145
4215
|
}
|
|
4216
|
+
/**
|
|
4217
|
+
* Where each of the messages named is and who wrote it, for text that names them by id; an id
|
|
4218
|
+
* that names no message is left out. Message files start with their ids, so only the files
|
|
4219
|
+
* asked for are read.
|
|
4220
|
+
*/
|
|
4221
|
+
async findMessages(ids) {
|
|
4222
|
+
const wanted = new Set(ids);
|
|
4223
|
+
const found = [];
|
|
4224
|
+
for await (const dir of this.messageDirs()) {
|
|
4225
|
+
for (const file of await listFiles(dir)) {
|
|
4226
|
+
if (!wanted.delete(file.slice(0, 26))) {
|
|
4227
|
+
continue;
|
|
4228
|
+
}
|
|
4229
|
+
const { data } = await readMarkdown(path6.join(dir, file), MessageFrontmatterSchema);
|
|
4230
|
+
found.push(MessageRefSchema.parse(data));
|
|
4231
|
+
}
|
|
4232
|
+
if (wanted.size === 0) {
|
|
4233
|
+
break;
|
|
4234
|
+
}
|
|
4235
|
+
}
|
|
4236
|
+
return found;
|
|
4237
|
+
}
|
|
4146
4238
|
/**
|
|
4147
4239
|
* What citizens have asked the user and the user has not answered, oldest first: every message
|
|
4148
4240
|
* by someone else that mentions the user, in an open thread or a channel the society or an
|
|
@@ -4644,12 +4736,12 @@ ${DASHBOARD_LINE}
|
|
|
4644
4736
|
continue;
|
|
4645
4737
|
}
|
|
4646
4738
|
const named = thread === null ? channelConversation(filedIn, message.channel) : void 0;
|
|
4647
|
-
let
|
|
4648
|
-
if (named !== void 0 &&
|
|
4649
|
-
|
|
4650
|
-
openChannels.set(message.channel,
|
|
4739
|
+
let open2 = named === void 0 ? void 0 : openChannels.get(message.channel);
|
|
4740
|
+
if (named !== void 0 && open2 === void 0) {
|
|
4741
|
+
open2 = await this.channelOpen(message.channel);
|
|
4742
|
+
openChannels.set(message.channel, open2);
|
|
4651
4743
|
}
|
|
4652
|
-
const part = thread ?? (
|
|
4744
|
+
const part = thread ?? (open2 === true ? `#${named}` : null);
|
|
4653
4745
|
if (conversation !== void 0 && (filedIn !== conversation.scope || part !== conversation.part)) {
|
|
4654
4746
|
continue;
|
|
4655
4747
|
}
|
|
@@ -4714,7 +4806,7 @@ ${task.body}`;
|
|
|
4714
4806
|
}
|
|
4715
4807
|
}
|
|
4716
4808
|
for (const file of await listFiles(this.paths.projectKnowledge(project))) {
|
|
4717
|
-
const content = await
|
|
4809
|
+
const content = await readFile4(path6.join(this.paths.projectKnowledge(project), file), "utf8");
|
|
4718
4810
|
if (content.toLowerCase().includes(needle)) {
|
|
4719
4811
|
hits.push({
|
|
4720
4812
|
kind: "knowledge",
|
|
@@ -4752,7 +4844,7 @@ ${task.body}`;
|
|
|
4752
4844
|
});
|
|
4753
4845
|
}
|
|
4754
4846
|
for (const entry of shared) {
|
|
4755
|
-
const content = await
|
|
4847
|
+
const content = await readFile4(entry.file, "utf8");
|
|
4756
4848
|
if (content.toLowerCase().includes(needle)) {
|
|
4757
4849
|
hits.push({
|
|
4758
4850
|
kind: entry.kind,
|
|
@@ -6241,7 +6333,7 @@ ${rationale}`}`);
|
|
|
6241
6333
|
throw new BoardError("NOT_FOUND", `no transcript of turn ${turnId} for ${agent}`);
|
|
6242
6334
|
}
|
|
6243
6335
|
const entries = [];
|
|
6244
|
-
for (const line of (await
|
|
6336
|
+
for (const line of (await readFile4(file, "utf8")).split("\n")) {
|
|
6245
6337
|
if (line.trim() === "")
|
|
6246
6338
|
continue;
|
|
6247
6339
|
const parsed = TranscriptEntrySchema.safeParse(JSON.parse(line));
|
|
@@ -6720,7 +6812,7 @@ ${rationale}`}`);
|
|
|
6720
6812
|
}
|
|
6721
6813
|
const file = entry.isFile() ? conflictCopyOf(relative) : null;
|
|
6722
6814
|
if (file !== null) {
|
|
6723
|
-
const since = await this.homes.changedAt(home, relative) ?? (await
|
|
6815
|
+
const since = await this.homes.changedAt(home, relative) ?? (await stat3(path6.join(home, relative)).catch(() => null))?.mtime.toISOString();
|
|
6724
6816
|
if (since !== void 0) {
|
|
6725
6817
|
found.push({ path: relative, file, since });
|
|
6726
6818
|
}
|
|
@@ -7298,7 +7390,7 @@ Short, curated, loaded on every turn. Keep entries that change future behavior a
|
|
|
7298
7390
|
if (!isSafeRelativePath(entry.path)) {
|
|
7299
7391
|
continue;
|
|
7300
7392
|
}
|
|
7301
|
-
const bytes = await
|
|
7393
|
+
const bytes = await readFile4(path6.join(dir, ...entry.path.split("/"))).catch(() => null);
|
|
7302
7394
|
const text = bytes === null || bytes.includes(0) ? null : bytes.toString("utf8");
|
|
7303
7395
|
files.push({
|
|
7304
7396
|
...entry,
|
|
@@ -7997,6 +8089,23 @@ ${charter.purpose}
|
|
|
7997
8089
|
yield* this.threadMessagesIn(this.paths.threads(slug), since);
|
|
7998
8090
|
}
|
|
7999
8091
|
}
|
|
8092
|
+
/** Every folder that holds messages: each channel's and each thread's, in the society and every project. */
|
|
8093
|
+
async *messageDirs() {
|
|
8094
|
+
for (const channel of await listDirs(this.paths.societyChannels())) {
|
|
8095
|
+
yield this.paths.societyChannel(channel);
|
|
8096
|
+
}
|
|
8097
|
+
for (const id of await listDirs(this.paths.societyThreads())) {
|
|
8098
|
+
yield this.paths.threadMessages(this.paths.societyThreads(), id);
|
|
8099
|
+
}
|
|
8100
|
+
for (const slug of await listDirs(this.paths.projects())) {
|
|
8101
|
+
for (const channel of await listDirs(this.paths.projectChannels(slug))) {
|
|
8102
|
+
yield this.paths.projectChannel(slug, channel);
|
|
8103
|
+
}
|
|
8104
|
+
for (const id of await listDirs(this.paths.threads(slug))) {
|
|
8105
|
+
yield this.paths.threadMessages(this.paths.threads(slug), id);
|
|
8106
|
+
}
|
|
8107
|
+
}
|
|
8108
|
+
}
|
|
8000
8109
|
async *threadMessagesIn(threads, since) {
|
|
8001
8110
|
for (const id of await listDirs(threads)) {
|
|
8002
8111
|
yield* await this.readMessagesIn(this.paths.threadMessages(threads, id), since);
|
|
@@ -11864,7 +11973,7 @@ function runnerRoutes(board2, hub, enrollments, enrollLimit) {
|
|
|
11864
11973
|
}
|
|
11865
11974
|
|
|
11866
11975
|
// apps/server/dist/static.js
|
|
11867
|
-
import { readFile as
|
|
11976
|
+
import { readFile as readFile5, stat as stat4 } from "node:fs/promises";
|
|
11868
11977
|
import path8 from "node:path";
|
|
11869
11978
|
var CONTENT_TYPES = {
|
|
11870
11979
|
".html": "text/html; charset=utf-8",
|
|
@@ -11881,7 +11990,7 @@ var CONTENT_TYPES = {
|
|
|
11881
11990
|
};
|
|
11882
11991
|
async function isFile(file) {
|
|
11883
11992
|
try {
|
|
11884
|
-
return (await
|
|
11993
|
+
return (await stat4(file)).isFile();
|
|
11885
11994
|
} catch {
|
|
11886
11995
|
return false;
|
|
11887
11996
|
}
|
|
@@ -11906,7 +12015,7 @@ function spaHandler(staticDir) {
|
|
|
11906
12015
|
const file = within(root, pathname);
|
|
11907
12016
|
const headers = { "x-content-type-options": "nosniff" };
|
|
11908
12017
|
if (file !== null && await isFile(file)) {
|
|
11909
|
-
return c.body(await
|
|
12018
|
+
return c.body(await readFile5(file), 200, {
|
|
11910
12019
|
...headers,
|
|
11911
12020
|
"content-type": CONTENT_TYPES[path8.extname(file)] ?? "application/octet-stream",
|
|
11912
12021
|
"cache-control": pathname.startsWith("/assets/") ? "public, max-age=31536000, immutable" : "no-cache"
|
|
@@ -11919,7 +12028,7 @@ function spaHandler(staticDir) {
|
|
|
11919
12028
|
if (!await isFile(index)) {
|
|
11920
12029
|
return c.text("The interface is not built: run pnpm build:web.", 404);
|
|
11921
12030
|
}
|
|
11922
|
-
return c.body(await
|
|
12031
|
+
return c.body(await readFile5(index), 200, {
|
|
11923
12032
|
...headers,
|
|
11924
12033
|
"content-type": CONTENT_TYPES[".html"] ?? "text/html",
|
|
11925
12034
|
"cache-control": "no-cache"
|
|
@@ -11937,6 +12046,7 @@ var ERROR_STATUS = {
|
|
|
11937
12046
|
INVALID_TRANSITION: 409,
|
|
11938
12047
|
INVALID_STATE: 409
|
|
11939
12048
|
};
|
|
12049
|
+
var EVENT_PAGE = 100;
|
|
11940
12050
|
var RetireBodySchema = z16.object({ reason: z16.string().min(1) });
|
|
11941
12051
|
var RunnerBodySchema = z16.object({ name: NameSchema });
|
|
11942
12052
|
var EnrollmentApprovalSchema = z16.object({ name: NameSchema });
|
|
@@ -11944,6 +12054,7 @@ var HomeRunnerBodySchema = z16.object({ runner: NameSchema.nullable() });
|
|
|
11944
12054
|
var ModelBodySchema = z16.object({ model: ModelNameSchema.nullable() });
|
|
11945
12055
|
var EffortBodySchema = z16.object({ effort: EffortSchema.nullable() });
|
|
11946
12056
|
var HistoryLimitSchema = z16.coerce.number().int().min(1).max(500);
|
|
12057
|
+
var MessageIdsSchema = z16.string().transform((ids) => ids.split(",").filter((id) => id !== "")).pipe(z16.array(UlidSchema).min(1).max(MESSAGE_LOOKUP_LIMIT));
|
|
11947
12058
|
var TimezoneBodySchema = z16.object({ timezone: TimeZoneSchema });
|
|
11948
12059
|
var ChannelBodySchema = z16.object({
|
|
11949
12060
|
project: NameSchema.nullable().default(null),
|
|
@@ -11957,6 +12068,7 @@ function bearer2(c) {
|
|
|
11957
12068
|
}
|
|
11958
12069
|
function createApp(deps) {
|
|
11959
12070
|
const { board: board2, version, turns: turns2, scheduler: scheduler2, models } = deps;
|
|
12071
|
+
const eventStreamPingMs = deps.eventStreamPingMs ?? 15e3;
|
|
11960
12072
|
const app2 = new Hono3();
|
|
11961
12073
|
app2.get("/health", (c) => c.json({ ok: true, version }));
|
|
11962
12074
|
app2.onError((error, c) => {
|
|
@@ -12054,6 +12166,7 @@ function createApp(deps) {
|
|
|
12054
12166
|
api.get("/proposals", async (c) => c.json(await board2.listProposals()));
|
|
12055
12167
|
api.get("/crons", async (c) => c.json(await board2.listCrons()));
|
|
12056
12168
|
api.get("/proposals/:id", async (c) => c.json(await board2.readProposal(c.req.param("id"))));
|
|
12169
|
+
api.get("/messages", async (c) => c.json(await board2.findMessages(MessageIdsSchema.parse(c.req.query("ids") ?? ""))));
|
|
12057
12170
|
api.get("/proposals/:id/files", async (c) => c.json(await board2.readSkillFiles(UlidSchema.parse(c.req.param("id")))));
|
|
12058
12171
|
api.get("/signals", async (c) => c.json(await board2.listSignals(Number(c.req.query("limit") ?? "100"))));
|
|
12059
12172
|
api.get("/metrics", async (c) => {
|
|
@@ -12158,16 +12271,41 @@ function createApp(deps) {
|
|
|
12158
12271
|
api.get("/events/stream", (c) => {
|
|
12159
12272
|
let cursor = c.req.query("since") ?? null;
|
|
12160
12273
|
return streamSSE2(c, async (stream) => {
|
|
12161
|
-
|
|
12162
|
-
|
|
12163
|
-
|
|
12164
|
-
|
|
12165
|
-
|
|
12166
|
-
|
|
12167
|
-
|
|
12168
|
-
|
|
12274
|
+
let appended = false;
|
|
12275
|
+
let wake = null;
|
|
12276
|
+
const unsubscribe = board2.onEventAppended(() => {
|
|
12277
|
+
appended = true;
|
|
12278
|
+
wake?.();
|
|
12279
|
+
});
|
|
12280
|
+
stream.onAbort(() => wake?.());
|
|
12281
|
+
try {
|
|
12282
|
+
if (cursor === "latest") {
|
|
12283
|
+
cursor = await board2.latestEventId();
|
|
12284
|
+
}
|
|
12285
|
+
while (!stream.aborted && !stream.closed) {
|
|
12286
|
+
appended = false;
|
|
12287
|
+
const events = await board2.readEvents(cursor === "" ? null : cursor, EVENT_PAGE);
|
|
12288
|
+
for (const event of events) {
|
|
12289
|
+
await stream.writeSSE({ id: event.id, event: event.type, data: JSON.stringify(event) });
|
|
12290
|
+
cursor = event.id;
|
|
12291
|
+
}
|
|
12292
|
+
if (appended || events.length === EVENT_PAGE) {
|
|
12293
|
+
continue;
|
|
12294
|
+
}
|
|
12295
|
+
const woken = await new Promise((resolve) => {
|
|
12296
|
+
const timer = setTimeout(() => resolve(false), eventStreamPingMs);
|
|
12297
|
+
wake = () => {
|
|
12298
|
+
clearTimeout(timer);
|
|
12299
|
+
resolve(true);
|
|
12300
|
+
};
|
|
12301
|
+
});
|
|
12302
|
+
wake = null;
|
|
12303
|
+
if (!woken && !stream.aborted) {
|
|
12304
|
+
await stream.writeSSE({ event: "ping", data: "" });
|
|
12305
|
+
}
|
|
12169
12306
|
}
|
|
12170
|
-
|
|
12307
|
+
} finally {
|
|
12308
|
+
unsubscribe();
|
|
12171
12309
|
}
|
|
12172
12310
|
});
|
|
12173
12311
|
});
|
package/package.json
CHANGED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import{It as e,x as t,xn as n}from"./format-DxXtbdNM.js";import{O as r,n as i,r as a,t as o,v as s}from"./index-BEVCPm4C.js";var c=n(),l=`grid size-7 shrink-0 place-items-center rounded-lg text-fg-tertiary hover:bg-surface-2/70 hover:text-fg-primary`;function u({fallback:n}){let u=e(),d=r({select:e=>e.location.state.__TSR_index}),f=a(),p=s(),m=t(),h=i(f,d);if(h===null)return n;let g=o(h,{entity:e=>p.get(e)?.title,project:e=>m.data?.find(t=>t.slug===e)?.name});return(0,c.jsx)(`button`,{type:`button`,onClick:()=>u.history.back(),"aria-label":`Back to ${g}`,title:`Back to ${g}`,className:l,children:`←`})}export{u as t};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{A as e,
|
|
1
|
+
import{A as e,Sn as t,Tn as n,bn as r,t as i,xn as a}from"./format-DxXtbdNM.js";import{a as o,o as s,s as c}from"./compose-_6feqP1t.js";import{t as l}from"./link-CTjFMNoY.js";import{n as u,r as d}from"./ThreadForms-1mbMZsbv.js";var f=n(t(),1),p=a();function m({project:t,onDone:n}){let{api:i}=e(),a=r(),l=s(),[m,h]=(0,f.useState)(``),[g,_]=(0,f.useState)(``),v=(0,f.useRef)(null);(0,f.useEffect)(()=>v.current?.focus(),[]);let y=c({mutationFn:()=>i.createChannel({project:t,name:m.trim(),purpose:g.trim()}),onSuccess:async e=>{await a.invalidateQueries({queryKey:[`channels`]}),n(),await l({to:`/c/$`,params:{_splat:e}})}});return(0,p.jsxs)(`form`,{"aria-label":`New channel`,className:`space-y-2 border-b border-line bg-surface-2/20 p-3`,onSubmit:e=>{e.preventDefault(),y.mutate()},children:[(0,p.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,p.jsx)(`div`,{className:`w-40 shrink-0`,children:(0,p.jsx)(`input`,{ref:v,value:m,maxLength:64,spellCheck:!1,"aria-label":`Channel name`,placeholder:`release-0-4`,onChange:e=>h(e.target.value),className:`${u} font-mono`})}),(0,p.jsx)(`input`,{value:g,"aria-label":`Purpose`,placeholder:`What the channel is for`,onChange:e=>_(e.target.value),className:`${u} min-w-0 flex-1`}),(0,p.jsx)(o,{onClick:n,children:`Cancel`}),(0,p.jsx)(o,{variant:`primary`,type:`submit`,disabled:y.isPending||m.trim()===``||g.trim()===``,children:y.isPending?`Opening…`:`Open`})]}),(0,p.jsx)(`p`,{className:`text-meta`,children:t===null?`Announced in the society's general, and followed by whoever subscribes to it; you read every channel without following one.`:`Announced in ${t}'s general, where its members choose to follow it; you read every channel without following one. Tasks filed in it hang their threads here, and it is archived once its tasks are done.`}),(0,p.jsx)(d,{error:y.error})]})}function h({channel:t,onDone:n}){let{api:i}=e(),a=r(),[s,l]=(0,f.useState)(``),m=c({mutationFn:()=>i.archiveChannel({channel:t,reason:s.trim()}),onSuccess:async()=>{await a.invalidateQueries({queryKey:[`channels`]}),n()}});return(0,p.jsxs)(`form`,{"aria-label":`Archive the channel`,className:`space-y-2 border-b border-line bg-surface-2/20 p-3`,onSubmit:e=>{e.preventDefault(),m.mutate()},children:[(0,p.jsxs)(`div`,{className:`flex items-center gap-2`,children:[(0,p.jsx)(`input`,{value:s,"aria-label":`Reason`,placeholder:`Why its work is done`,onChange:e=>l(e.target.value),className:`${u} min-w-0 flex-1`}),(0,p.jsx)(o,{onClick:n,children:`Cancel`}),(0,p.jsx)(o,{variant:`primary`,type:`submit`,disabled:m.isPending||s.trim()===``,children:m.isPending?`Archiving…`:`Archive channel`})]}),(0,p.jsx)(`p`,{className:`text-meta`,children:`Its open threads close, nobody follows it, and nothing more is posted or filed here; its posts, threads, and tasks stay readable. The board refuses while a task filed here is in play.`}),(0,p.jsx)(d,{error:m.error})]})}function g({channels:e,now:t}){return e.length===0?(0,p.jsx)(`p`,{className:`text-meta`,children:`No channels.`}):(0,p.jsx)(`ul`,{className:`space-y-1`,children:e.map(e=>(0,p.jsxs)(`li`,{children:[(0,p.jsxs)(l,{to:`/c/$`,params:{_splat:e.ref},className:`text-sm hover:text-fg-primary ${e.archived===void 0?`text-fg-secondary`:`text-fg-tertiary`}`,children:[`# `,e.name]}),(0,p.jsxs)(`span`,{className:`text-meta`,children:[` `,`· `,e.messages,` messages`,e.archived===void 0?``:` · archived ${i(e.archived.at,t)}`]})]},e.ref))})}export{g as n,m as r,h as t};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{A as e,H as t,Sn as n,Tn as r,_ as i,b as a,c as o,en as s,ht as c,t as l,x as u,xn as d,z as f}from"./format-DxXtbdNM.js";import{a as p}from"./compose-_6feqP1t.js";import{t as m}from"./link-CTjFMNoY.js";import{A as h,C as g,i as _,k as v,s as y}from"./index-BEVCPm4C.js";import{i as b}from"./ThreadForms-1mbMZsbv.js";import{t as x}from"./ChannelForms-BYrcUldk.js";import{t as S}from"./MessageItem-D7MUCA_T.js";import{n as C,t as w}from"./Pane-_D7n5uKH.js";import{n as T}from"./ThreadItem-QJSA92C4.js";import{t as E}from"./useStickyScroll-Ca7sWbGH.js";var D=r(n(),1);function O(e,t){let n=e.notices.at(-1);return n!==void 0&&n.kind===`knowledge`&&t.kind===`knowledge`&&n.action!==`removed`&&t.action===`updated`&&n.topic===t.topic&&n.project===t.project}function k(e,t){let n=[],r=null;for(let i of e){let e=t(i),a=e?.notice;e===void 0||a===void 0?(n.push({kind:`item`,item:i}),r=null):r!==null&&O(r,a)?(r.messages.push(e),r.notices.push(a)):(r={messages:[e],notices:[a]},n.push({kind:`notices`,run:r}))}return n}function A(e){let[t]=e,n=e.length-1;return t===void 0||t.action===`removed`?{did:`removed`,times:null}:t.action===`written`?{did:`wrote`,times:n===0?null:`then updated it ${n===1?`once`:`${n} times`}`}:{did:`updated`,times:n===0?null:`${e.length} times`}}function j(e){let t=[...new Set(e)];return t.length<=2?t.join(` and `):`${t.slice(0,-1).join(`, `)}, and ${t.at(-1)??``}`}var M=d();function N(){return(0,M.jsx)(`svg`,{viewBox:`0 0 16 16`,"aria-hidden":`true`,className:`size-3.5`,children:(0,M.jsx)(`path`,{d:`M8 4.2C6.4 3 4.4 2.8 2.2 3.4v9.2c2.2-.6 4.2-.4 5.8.8m0-9.2c1.6-1.2 3.6-1.4 5.8-.8v9.2c-2.2-.6-4.2-.4-5.8.8m0-9.2v9.2`,fill:`none`,stroke:`currentColor`,strokeWidth:`1.2`,strokeLinejoin:`round`})})}function P({notices:e}){let[t]=e;if(t===void 0)return null;let{did:n,times:r}=A(e);return(0,M.jsxs)(M.Fragment,{children:[n,` knowledge`,` `,t.action===`removed`?(0,M.jsx)(`span`,{className:`text-fg-secondary`,children:t.topic}):(0,M.jsx)(m,{to:`/knowledge/$scope/$topic`,params:{scope:t.project??`society`,topic:t.topic},className:`text-fg-secondary hover:text-fg-primary`,children:t.topic}),r===null?null:` · ${r}`]})}function F(e){let t=e.notices.filter(e=>e.kind===`knowledge`);return{mark:(0,M.jsx)(N,{}),text:(0,M.jsx)(P,{notices:t})}}function I({run:e,now:t}){let n=y(),r=e.messages.at(-1);if(r===void 0)return null;let{mark:i,text:a}=F(e);return(0,M.jsxs)(`div`,{className:`flex gap-3 px-4 py-1`,children:[(0,M.jsx)(`span`,{className:`grid h-5 w-7 shrink-0 place-items-center text-fg-muted`,children:i}),(0,M.jsxs)(`div`,{className:`flex min-w-0 flex-1 items-baseline gap-3`,children:[(0,M.jsxs)(`p`,{className:`min-w-0 flex-1 truncate text-sm text-fg-tertiary`,children:[j(e.messages.map(e=>n(e.author))),` `,a]}),(0,M.jsx)(`time`,{dateTime:r.ts,title:e.messages.map(e=>`${n(e.author)} ${e.ts}`).join(`
|
|
2
|
+
`),className:`shrink-0 text-meta`,children:l(r.ts,t)})]})]})}function L(){let{_splat:e=`general`}=h({from:`/c/$`}),{message:t}=v({from:`/c/$`});return(0,M.jsx)(R,{channel:e,target:t},e)}function R({channel:n,target:r}){let{api:d}=e(),m=s({queryKey:[`channel`,n],queryFn:()=>d.channel(n)}),h=f(),v=i(),y=u(),O=o(),A=a(3e4),[j,N]=(0,D.useState)(!1),[P,F]=(0,D.useState)(!1),L=(0,D.useMemo)(()=>{let e=(m.data??[]).map(e=>({kind:`message`,at:e.ts,id:e.id,message:e})),t=(h.data??[]).filter(e=>e.channel===n).map(e=>({kind:`thread`,at:e.openedAt,id:e.id,thread:e}));return[...e,...t].toSorted((e,t)=>e.at.localeCompare(t.at)||e.id.localeCompare(t.id))},[m.data,h.data,n]),R=(0,D.useMemo)(()=>k(L,e=>e.kind===`message`?e.message:void 0),[L]),z=m.data?.at(-1)?.id??null;(0,D.useEffect)(()=>g(n,z),[n,z]);let{ref:B,onScroll:V}=E(r===void 0),[H,U]=n.split(`/`),W=U??H??n,G=U===void 0?void 0:y.data?.find(e=>e.slug===H),K=U===void 0?`society`:G?.name??H,q=O.data?.find(e=>e.ref===n)?.archived,J=G?.archived!==void 0||q!==void 0,Y=!J&&!c(U===void 0?null:H??null,W),X=L.filter(e=>e.kind===`thread`&&e.thread.state===`open`);return(0,M.jsxs)(M.Fragment,{children:[(0,M.jsx)(w,{title:`# ${W}`,subtitle:`${K} · ${m.data?.length??0} messages${X.length>0?` · ${X.length} open threads`:``}`,trailing:j||P||J?null:(0,M.jsxs)(M.Fragment,{children:[Y?(0,M.jsx)(p,{onClick:()=>F(!0),children:`Archive…`}):null,(0,M.jsx)(p,{onClick:()=>N(!0),children:`New thread`})]})}),j?(0,M.jsx)(b,{channel:n,onDone:()=>N(!1)}):null,P?(0,M.jsx)(x,{channel:n,onDone:()=>F(!1)}):null,(0,M.jsx)(`div`,{ref:B,onScroll:V,className:`flex-1 overflow-y-auto py-2`,children:m.error instanceof t&&m.error.status===404?(0,M.jsxs)(C,{children:[`There is no channel `,n,`.`]}):m.data===void 0?(0,M.jsx)(C,{children:`Reading the channel…`}):L.length===0?(0,M.jsx)(C,{children:`Nothing has been said here yet.`}):R.map(e=>{if(e.kind===`notices`){let t=e.run.messages[0]?.id??``;return(0,M.jsx)(I,{run:e.run,now:A},t)}let{item:t}=e;return t.kind===`message`?(0,M.jsx)(S,{message:t.message,members:v.data,now:A,target:t.id===r},t.id):(0,M.jsx)(T,{thread:t.thread,now:A},t.id)})}),m.data===void 0?null:q===void 0?J?(0,M.jsxs)(`p`,{className:`border-t border-line px-4 py-3 text-meta`,children:[K,` is archived, so its channels are read-only.`]}):(0,M.jsx)(_,{target:{channel:n},placeholder:`Message #${W}`}):(0,M.jsxs)(`p`,{className:`border-t border-line px-4 py-3 text-meta`,children:[`Archived `,l(q.at,A),` by `,q.by,`: `,q.reason,`. Its posts, threads, and tasks stay readable; nothing more is posted or filed here.`]})]})}export{L as ChannelView};
|