@timqi/pier 0.0.27 → 0.0.28

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.
Files changed (68) hide show
  1. package/dist/agent/config-sync.js +28 -8
  2. package/dist/agent/config.js +64 -15
  3. package/dist/core/identity.js +17 -1
  4. package/dist/db.js +9 -0
  5. package/dist/main.js +1 -0
  6. package/dist/settings.js +4 -1
  7. package/dist/tasks/service.js +4 -0
  8. package/dist/tasks/store.js +12 -0
  9. package/dist/tools.js +17 -3
  10. package/dist/web/public/assets/{activity-B44nbhXy.js → activity-DVamP8fs.js} +2 -2
  11. package/dist/web/public/assets/activity-DVamP8fs.js.br +0 -0
  12. package/dist/web/public/assets/activity-DVamP8fs.js.gz +0 -0
  13. package/dist/web/public/assets/boards-BKtjPwus.js +1 -0
  14. package/dist/web/public/assets/boards-BKtjPwus.js.br +0 -0
  15. package/dist/web/public/assets/boards-BKtjPwus.js.gz +0 -0
  16. package/dist/web/public/assets/explorer-CjO9NAKQ.js +4 -0
  17. package/dist/web/public/assets/explorer-CjO9NAKQ.js.br +0 -0
  18. package/dist/web/public/assets/explorer-CjO9NAKQ.js.gz +0 -0
  19. package/dist/web/public/assets/{index-CZeL3DfU.css → index-C23sXyS3.css} +1 -1
  20. package/dist/web/public/assets/index-C23sXyS3.css.br +0 -0
  21. package/dist/web/public/assets/index-C23sXyS3.css.gz +0 -0
  22. package/dist/web/public/assets/index-DzWnvhXb.js +85 -0
  23. package/dist/web/public/assets/index-DzWnvhXb.js.br +0 -0
  24. package/dist/web/public/assets/index-DzWnvhXb.js.gz +0 -0
  25. package/dist/web/public/assets/runs-CxIZAx1s.js +1 -0
  26. package/dist/web/public/assets/runs-CxIZAx1s.js.br +0 -0
  27. package/dist/web/public/assets/runs-CxIZAx1s.js.gz +0 -0
  28. package/dist/web/public/assets/settings-B8JWLbpL.js +5 -0
  29. package/dist/web/public/assets/settings-B8JWLbpL.js.br +0 -0
  30. package/dist/web/public/assets/settings-B8JWLbpL.js.gz +0 -0
  31. package/dist/web/public/assets/{task-runs-BW06ZEVz.js → task-runs-BZ1brkLC.js} +2 -2
  32. package/dist/web/public/assets/task-runs-BZ1brkLC.js.br +0 -0
  33. package/dist/web/public/assets/task-runs-BZ1brkLC.js.gz +0 -0
  34. package/dist/web/public/assets/tasks-BoYiBQpq.js +4 -0
  35. package/dist/web/public/assets/tasks-BoYiBQpq.js.br +0 -0
  36. package/dist/web/public/assets/tasks-BoYiBQpq.js.gz +0 -0
  37. package/dist/web/public/index.html +7 -6
  38. package/dist/web/public/index.html.br +0 -0
  39. package/dist/web/public/index.html.gz +0 -0
  40. package/dist/web/server.js +29 -62
  41. package/dist/web/session-state.js +14 -32
  42. package/package.json +1 -1
  43. package/dist/web/public/assets/activity-B44nbhXy.js.br +0 -0
  44. package/dist/web/public/assets/activity-B44nbhXy.js.gz +0 -0
  45. package/dist/web/public/assets/boards-CeiKKBaY.js +0 -1
  46. package/dist/web/public/assets/boards-CeiKKBaY.js.br +0 -0
  47. package/dist/web/public/assets/boards-CeiKKBaY.js.gz +0 -0
  48. package/dist/web/public/assets/code-I0lxXuvE.js +0 -1
  49. package/dist/web/public/assets/explorer-BOeDogsY.js +0 -5
  50. package/dist/web/public/assets/explorer-BOeDogsY.js.br +0 -0
  51. package/dist/web/public/assets/explorer-BOeDogsY.js.gz +0 -0
  52. package/dist/web/public/assets/index-BmKvoulH.js +0 -84
  53. package/dist/web/public/assets/index-BmKvoulH.js.br +0 -0
  54. package/dist/web/public/assets/index-BmKvoulH.js.gz +0 -0
  55. package/dist/web/public/assets/index-CZeL3DfU.css.br +0 -0
  56. package/dist/web/public/assets/index-CZeL3DfU.css.gz +0 -0
  57. package/dist/web/public/assets/runs-C4OlQT7W.js +0 -1
  58. package/dist/web/public/assets/runs-C4OlQT7W.js.br +0 -0
  59. package/dist/web/public/assets/runs-C4OlQT7W.js.gz +0 -0
  60. package/dist/web/public/assets/settings-Cn8i_QiP.js +0 -6
  61. package/dist/web/public/assets/settings-Cn8i_QiP.js.br +0 -0
  62. package/dist/web/public/assets/settings-Cn8i_QiP.js.gz +0 -0
  63. package/dist/web/public/assets/task-runs-BW06ZEVz.js.br +0 -0
  64. package/dist/web/public/assets/task-runs-BW06ZEVz.js.gz +0 -0
  65. package/dist/web/public/assets/tasks-DZzYlKsd.js +0 -4
  66. package/dist/web/public/assets/tasks-DZzYlKsd.js.br +0 -0
  67. package/dist/web/public/assets/tasks-DZzYlKsd.js.gz +0 -0
  68. package/dist/web/repos.js +0 -75
@@ -52,20 +52,30 @@ function project(value, where, onPrivate) {
52
52
  return [[key, project(entry, `${where} ${key}`, onPrivate)]];
53
53
  }));
54
54
  }
55
+ /** A provider id that cannot address anything but its own record. */
56
+ function providerId(id) {
57
+ if (typeof id !== "string" || id.length > 100 || !/^[a-z0-9][a-z0-9._-]*$/.test(id) ||
58
+ ["constructor", "prototype"].includes(id)) {
59
+ throw new Error("invalid snapshot provider id");
60
+ }
61
+ return id;
62
+ }
63
+ /** A model id both sides can address — in a catalog or as the default. */
64
+ function modelId(id) {
65
+ if (typeof id !== "string" || !id.trim() || id !== id.trim() || id.length > 100) {
66
+ throw new Error("model id must be a non-empty trimmed string");
67
+ }
68
+ return id;
69
+ }
55
70
  /** The shape both sides must agree on: a provider id that cannot address
56
71
  * anything but its own record, and a catalog addressable by model id. */
57
72
  function portable(id, value, onPrivate) {
58
- if (id.length > 100 || !/^[a-z0-9][a-z0-9._-]*$/.test(id) || ["constructor", "prototype"].includes(id)) {
59
- throw new Error("invalid snapshot provider id");
60
- }
73
+ providerId(id);
61
74
  const provider = record(project(value, `provider ${id}`, onPrivate), `provider ${id}`);
62
75
  const models = provider.models ?? [];
63
76
  if (!Array.isArray(models))
64
77
  throw new Error("models must be an array");
65
- const ids = models.map((model) => record(model, "model").id);
66
- if (ids.some((modelId) => typeof modelId !== "string" || !modelId.trim() || modelId !== modelId.trim())) {
67
- throw new Error("model id must be a non-empty trimmed string");
68
- }
78
+ const ids = models.map((model) => modelId(record(model, "model").id));
69
79
  if (new Set(ids).size !== ids.length)
70
80
  throw new Error("duplicate model ids");
71
81
  const overrides = provider.modelOverrides ?? {};
@@ -91,11 +101,20 @@ export function snapshotProviders(providers = {}) {
91
101
  }
92
102
  return shared;
93
103
  }
104
+ /** A default model names a provider and a model; the provider is not required
105
+ * to be in models.json, because Pi's own built-ins never are. */
106
+ function normalizeDefaultModel(raw) {
107
+ if (raw === null)
108
+ return null;
109
+ const ref = record(raw, "default model");
110
+ only(ref, ["provider", "id"], "default model");
111
+ return { provider: providerId(ref.provider), id: modelId(ref.id) };
112
+ }
94
113
  /** Strict import boundary: a source that states a private field is refused,
95
114
  * never trusted as a mask over the local one. */
96
115
  export function normalizeAgentSnapshot(raw) {
97
116
  const snapshot = record(raw, "agent snapshot");
98
- only(snapshot, ["files", "providers"], "agent snapshot");
117
+ only(snapshot, ["files", "providers", "defaultModel"], "agent snapshot");
99
118
  const files = record(snapshot.files, "snapshot files");
100
119
  only(files, ["SYSTEM.md", "AGENTS.md"], "snapshot files");
101
120
  for (const name of ["SYSTEM.md", "AGENTS.md"]) {
@@ -110,6 +129,7 @@ export function normalizeAgentSnapshot(raw) {
110
129
  return {
111
130
  files: { "SYSTEM.md": files["SYSTEM.md"], "AGENTS.md": files["AGENTS.md"] },
112
131
  providers,
132
+ ...(snapshot.defaultModel !== undefined ? { defaultModel: normalizeDefaultModel(snapshot.defaultModel) } : {}),
113
133
  };
114
134
  }
115
135
  /** A model or override arrives whole; only the local private fields survive it. */
@@ -9,7 +9,9 @@ import { pierPath } from "../paths.js";
9
9
  import { mergeSnapshotProviders, normalizeAgentSnapshot, snapshotProviders } from "./config-sync.js";
10
10
  const GLOBAL_FILES = ["SYSTEM.md", "AGENTS.md", "settings.json", "models.json"];
11
11
  const PROJECT_FILES = ["AGENTS.md"];
12
- const SNAPSHOT_FILES = ["SYSTEM.md", "AGENTS.md", "models.json"];
12
+ // settings.json is on the list for one field: the default model. Everything
13
+ // else in it is machine-local and survives an import untouched.
14
+ const SNAPSHOT_FILES = ["SYSTEM.md", "AGENTS.md", "models.json", "settings.json"];
13
15
  const RESOURCE_DEPTH = 3; // extensions/skills nest at most a couple of levels
14
16
  /** Pier owns the Pi runtime dir: config lives in the syncable `~/.pier/pi`
15
17
  * repo, not `~/.pi`. main.ts exports this as PI_CODING_AGENT_DIR so the SDK's
@@ -211,13 +213,14 @@ export class PiConfigStore {
211
213
  }
212
214
  exportSnapshot() {
213
215
  return this.#withWrite(async () => {
214
- const [system, agents, raw] = await Promise.all(SNAPSHOT_FILES.map((name) => readNullable(join(this.agentDir, name))));
216
+ const [system, agents, raw, rawSettings] = await Promise.all(SNAPSHOT_FILES.map((name) => readNullable(join(this.agentDir, name))));
215
217
  const parsed = raw?.trim() ? parseModels(raw) : {};
216
218
  if (!parsed)
217
219
  throw new Error("models.json must be valid JSON before exporting configuration");
218
220
  return normalizeAgentSnapshot({
219
221
  files: { "SYSTEM.md": system, "AGENTS.md": agents },
220
222
  providers: snapshotProviders(parsed.providers),
223
+ defaultModel: defaultModelRef(rawSettings),
221
224
  });
222
225
  });
223
226
  }
@@ -233,7 +236,13 @@ export class PiConfigStore {
233
236
  const providers = mergeSnapshotProviders(parsed.providers, incoming.providers);
234
237
  const models = JSON.stringify(providers) === JSON.stringify(parsed.providers ?? {})
235
238
  ? raw : `${JSON.stringify({ ...parsed, providers }, null, 2)}\n`;
236
- const after = [incoming.files["SYSTEM.md"], incoming.files["AGENTS.md"], models];
239
+ const rawSettings = before[3];
240
+ // A source that never states a default (an older one) leaves the local
241
+ // settings.json alone; `null` is a stated "no default" and clears it.
242
+ const settings = incoming.defaultModel === undefined
243
+ ? rawSettings
244
+ : withDefaultModel(rawSettings, incoming.defaultModel);
245
+ const after = [incoming.files["SYSTEM.md"], incoming.files["AGENTS.md"], models, settings];
237
246
  const changes = names.flatMap((name, index) => before[index] === after[index] ? [] : [{
238
247
  path: join(this.agentDir, name), before: before[index], after: after[index],
239
248
  temp: join(this.agentDir, `${name}.${process.pid}.${randomUUID()}.tmp`),
@@ -450,21 +459,61 @@ function restoreHeaders(incoming, current) {
450
459
  headers[name] = original;
451
460
  }
452
461
  }
453
- function parseModels(raw) {
462
+ /** settings.json is read for one pair and never rewritten wholesale. Malformed
463
+ * JSON is refused rather than read as "no default": that would publish, or
464
+ * import, a cleared default nobody asked for. */
465
+ function readSettings(raw) {
466
+ if (!raw?.trim())
467
+ return {};
468
+ const parsed = parseObject(raw);
469
+ if (!parsed)
470
+ throw new Error("settings.json must be valid JSON before synchronizing configuration");
471
+ return parsed;
472
+ }
473
+ /** Pi writes defaultProvider and defaultModel as a pair; half of one is a hand
474
+ * edit that a shared default cannot represent, so it is refused. */
475
+ function defaultModelRef(raw) {
476
+ const settings = readSettings(raw);
477
+ const provider = settings.defaultProvider;
478
+ const id = settings.defaultModel;
479
+ if (provider === undefined && id === undefined)
480
+ return null;
481
+ if (typeof provider !== "string" || typeof id !== "string") {
482
+ throw new Error("settings.json must name defaultProvider and defaultModel together as strings");
483
+ }
484
+ return { provider, id };
485
+ }
486
+ /** The incoming default replaces the local pair and nothing else; unchanged
487
+ * content returns the original bytes so the import stays a no-op. */
488
+ function withDefaultModel(raw, model) {
489
+ const settings = readSettings(raw);
490
+ const next = { ...settings };
491
+ if (model) {
492
+ next.defaultProvider = model.provider;
493
+ next.defaultModel = model.id;
494
+ }
495
+ else {
496
+ delete next.defaultProvider;
497
+ delete next.defaultModel;
498
+ }
499
+ return JSON.stringify(next) === JSON.stringify(settings) ? raw : `${JSON.stringify(next, null, 2)}\n`;
500
+ }
501
+ const parseObject = (raw) => {
454
502
  try {
455
- const parsed = JSON.parse(raw);
456
- if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed))
457
- return null;
458
- const providers = parsed.providers;
459
- if (providers !== undefined) {
460
- if (typeof providers !== "object" || providers === null || Array.isArray(providers))
461
- return null;
462
- if (Object.values(providers).some((provider) => typeof provider !== "object" || provider === null || Array.isArray(provider)))
463
- return null;
464
- }
465
- return parsed;
503
+ return asRecord(JSON.parse(raw)) ?? null;
466
504
  }
467
505
  catch {
468
506
  return null;
469
507
  }
508
+ };
509
+ function parseModels(raw) {
510
+ const parsed = parseObject(raw);
511
+ if (!parsed)
512
+ return null;
513
+ if (parsed.providers !== undefined) {
514
+ const providers = asRecord(parsed.providers);
515
+ if (!providers || Object.values(providers).some((provider) => !asRecord(provider)))
516
+ return null;
517
+ }
518
+ return parsed;
470
519
  }
@@ -90,6 +90,12 @@ export function splitSpeaker(text) {
90
90
  text: text.slice(m[0].length),
91
91
  };
92
92
  }
93
+ /** Trailing lines that open a `[name](file://` link — or a last line that is
94
+ * only `[name` because the clip fell inside it. */
95
+ const ATTACHMENT_LINES = /(?:\n\[[^\]\n]*(?:\]\(\s*<?file:\/\/[^\n]*|$))+$/;
96
+ /** A long code span that opens the message, when something else follows it.
97
+ * Long, because `npm test` before "fails" is the subject, not a paste. */
98
+ const LEADING_CODE_SPAN = /^\s*`[^`\n]{40,}`\s+(\S[\s\S]*)$/;
93
99
  /**
94
100
  * A session titled by its first prompt inherits that prompt's header, and the
95
101
  * header is for the model: anything a person reads — a list row, a session
@@ -97,6 +103,11 @@ export function splitSpeaker(text) {
97
103
  * the workbench ever opened. So the speaker comes off and what they said is
98
104
  * the title. Here rather than in a UI module because the push notification
99
105
  * needs the same answer and a second copy of this would drift (AGENTS.md §3).
106
+ *
107
+ * Two more things a first prompt carries that a title should not: the
108
+ * attachment lines a channel appended (`[name](file:///…)`, often cut mid-name
109
+ * by the title clip), and a pasted log line in backticks ahead of the actual
110
+ * question — dropped only when a question follows; a paste alone stays.
100
111
  */
101
112
  export function readableTitle(title) {
102
113
  if (!title)
@@ -106,5 +117,10 @@ export function readableTitle(title) {
106
117
  // change what the sidebar's search is matching against for nothing.
107
118
  if (text === title)
108
119
  return title;
109
- return text.replace(/\s+/g, " ").trim() || title;
120
+ const said = text
121
+ .replace(ATTACHMENT_LINES, "")
122
+ .replace(LEADING_CODE_SPAN, "$1")
123
+ .replace(/\s+/g, " ")
124
+ .trim();
125
+ return said || title;
110
126
  }
package/dist/db.js CHANGED
@@ -322,6 +322,15 @@ const MIGRATIONS = [
322
322
  -- all of them: the query is by the delegating session, which lived only in
323
323
  -- the JSON — without this every session open was a full scan of task_runs.
324
324
  CREATE INDEX task_runs_invoked_by ON task_runs(json_extract(json, '$.invokedBySessionId'), queued_at DESC);
325
+ `,
326
+ // 19 — the rail is one flat list, and pinned means "stuck to the top".
327
+ `
328
+ -- pinned used to mean "listed under Projects", and every session created in
329
+ -- the workbench was. Now it means on top of the list, and nobody put a
330
+ -- historical session there: kept as-is, every web session ever made would
331
+ -- land in the pinned section. project_sort stays as a column nothing reads
332
+ -- — a SQLite column drop rewrites the table for a NULL nobody pays for.
333
+ UPDATE session_state SET pinned = 0;
325
334
  `,
326
335
  ];
327
336
  /**
package/dist/main.js CHANGED
@@ -382,6 +382,7 @@ app.route("/", createServer({
382
382
  reload: () => reloadInstance(true),
383
383
  backgroundRuns: (id) => tasks.backgroundRuns(id),
384
384
  activeBackgroundRunCounts: () => tasks.activeBackgroundRunCounts(),
385
+ taskSessions: () => tasks.taskSessions(),
385
386
  channelOf: (id) => conversations.channelOf(id),
386
387
  }));
387
388
  const port = Number(process.env.PORT ?? 3141);
package/dist/settings.js CHANGED
@@ -113,7 +113,10 @@ export class SettingsStore {
113
113
  autoUpdate: this.#value("autoUpdate") === "1",
114
114
  extensions: this.#json("extensions", normalizeExtensions, "a list of names") ?? [],
115
115
  tools: this.#json("tools", normalizeTools, "a list of names") ?? [],
116
- customTools: this.#json("customTools", normalizeCustomTools, "a list of {name, spec}") ?? [],
116
+ // `"drop"`: a stored row whose name the bundled catalog has since taken
117
+ // is redundant, not malformed — rejecting the setting over it would take
118
+ // every other tool declared beside it (see normalizeCustomTools).
119
+ customTools: this.#json("customTools", (raw) => normalizeCustomTools(raw, [], "drop"), "a list of {name, spec}") ?? [],
117
120
  };
118
121
  }
119
122
  /**
@@ -217,6 +217,10 @@ export class TaskService {
217
217
  activeBackgroundRunCounts() {
218
218
  return this.store.countActiveBackgroundRunsBySession();
219
219
  }
220
+ /** The sessions runs made for themselves, for a list of the operator's own. */
221
+ taskSessions() {
222
+ return this.store.taskOwnedSessionIds();
223
+ }
220
224
  run(taskId, input = null, source = "manual", parentRunId = null, provenance = {}) {
221
225
  this.refusePaused(parentRunId);
222
226
  const task = this.get(taskId);
@@ -143,6 +143,18 @@ export class TaskStore {
143
143
  `).all();
144
144
  return new Map(rows.map((row) => [row.session_id, row.n]));
145
145
  }
146
+ /** Every session a run created for itself — `fresh` is the one mode that
147
+ * makes a session rather than borrowing one, and a resumed run reuses the
148
+ * session its `fresh` predecessor already stamped. These are the agent's
149
+ * conversations with itself, which the rail does not list. */
150
+ taskOwnedSessionIds() {
151
+ const rows = this.sql(`
152
+ SELECT DISTINCT json_extract(json, '$.context.sessionId') AS id
153
+ FROM task_runs
154
+ WHERE json_extract(json, '$.sessionMode') = 'fresh' AND id IS NOT NULL
155
+ `).all();
156
+ return new Set(rows.map((row) => row.id));
157
+ }
146
158
  listRunsForSession(sessionId, limit = 50) {
147
159
  return this.#many(`
148
160
  SELECT json FROM task_runs
package/dist/tools.js CHANGED
@@ -127,7 +127,18 @@ export const CUSTOM_TOOL_RULES = `each custom tool needs a name (letters, digits
127
127
  export function normalizeCustomTools(raw,
128
128
  /** Names this instance already answers to that this file cannot see — the
129
129
  * bundled extensions live behind the Pi SDK, so main.ts hands them in. */
130
- reserved = []) {
130
+ reserved = [],
131
+ /**
132
+ * What a name Pier already manages means. At the route: a refusal, because
133
+ * the operator is declaring it now and can pick another. Reading a stored
134
+ * row: `"drop"`, because the catalog grew into that name *after* the row was
135
+ * written — jq shipped as a bundled tool and turned the operator's own jq
136
+ * row into a whole setting Pier refused, dropping every *other* tool
137
+ * declared beside it. The bundled row installs the same binary, so the
138
+ * entry is redundant rather than wrong. Left in the row on purpose: a Pier
139
+ * that stops bundling that name finds the declaration still there.
140
+ */
141
+ managedName = "reject") {
131
142
  // Case-insensitively: two names differing only in case are one filename on a
132
143
  // case-insensitive filesystem, and one switch whose meaning depends on which
133
144
  // ran last.
@@ -145,8 +156,11 @@ reserved = []) {
145
156
  const cleanName = name.trim();
146
157
  if (!TOOL_NAME.test(cleanName))
147
158
  return null;
148
- if (taken.has(cleanName.toLowerCase()))
149
- return null;
159
+ if (taken.has(cleanName.toLowerCase())) {
160
+ if (managedName === "reject")
161
+ return null;
162
+ continue;
163
+ }
150
164
  if (tools.some((tool) => tool.name.toLowerCase() === cleanName.toLowerCase()))
151
165
  return null;
152
166
  // The migration: a stored `{name, spec}` is the block it always meant.
@@ -1,5 +1,5 @@
1
- import{B as e,C as t,H as n,I as r,K as i,L as a,N as o,k as s,m as c,v as l,z as u}from"./index-BmKvoulH.js";var d=(e,t={})=>{let n=document.createElementNS(`http://www.w3.org/2000/svg`,e);for(let[e,r]of Object.entries(t))n.setAttribute(e,r);return n},f=e=>e===null?`-`:r(Date.now()-e),p={light:{edge:`#a3a3a3`,callback:`#0891b2`,message:`#d97706`,scheduler:{fill:`#f5f3ff`,stroke:`#a78bfa`,text:`#5b21b6`},console:{fill:`#eff6ff`,stroke:`#93c5fd`,text:`#1d4ed8`},task:{fill:`#fffbeb`,stroke:`#fbbf24`,text:`#92400e`},process:{fill:`#f5f5f5`,stroke:`#a3a3a3`,text:`#525252`,dash:`4 3`},streaming:{fill:`#ffffff`,stroke:`#10b981`,text:`#262626`},idle:{fill:`#fafafa`,stroke:`#d4d4d4`,text:`#737373`}},dark:{edge:`#7a7a7a`,callback:`#3fc3dd`,message:`#e0a13a`,scheduler:{fill:`#2b2440`,stroke:`#7d63c9`,text:`#c9b8f5`},console:{fill:`#1f2a3d`,stroke:`#4a7fbf`,text:`#a8c8f0`},task:{fill:`#3a2f1c`,stroke:`#b8862a`,text:`#f0d49a`},process:{fill:`#262626`,stroke:`#5c5c5c`,text:`#a3a3a3`,dash:`4 3`},streaming:{fill:`#22302a`,stroke:`#10b981`,text:`#e5e5e5`},idle:{fill:`#212121`,stroke:`#4a4a4a`,text:`#9a9a9a`}}},m=()=>p[document.documentElement.dataset.theme===`dark`?`dark`:`light`];function h(r,p,h){let g=`sessions`,_=`active`,v={sessions:[],runs:[],messages:[]},y=``,b=e(async()=>{let e=_,t=await n(`/api/activity?scope=${e}`,`Failed to load activity`);if(!t.ok){y=``,r.replaceChildren(a(`p`,`p-4 text-[13px] text-red-600`,t.error));return}if(e!==_)return;let o=JSON.stringify(t.value);if(`${e}:${o}`===y)return;y=`${e}:${o}`;let s=t.value;v={...s,sessions:s.sessions.map(e=>({...e,title:i(e.title)}))},x()});function x(){let e=s(t([[`Sessions`,`sessions`],[`Relationships`,`dependencies`]],g,e=>{g=e,x()}),a(`span`,`ml-auto text-[11.5px] text-neutral-400`,`${v.sessions.length} sessions · ${v.runs.length} runs`),t([[`Current`,`active`],[`Last 24h`,`recent`]],_,e=>{_=e,x(),b()})),n=a(`div`,`min-h-0 flex-1 overflow-auto`);g===`sessions`?S(n):w(n),r.replaceChildren(e,n)}function S(e){let t=document.createElement(`table`);t.className=`w-full table-fixed text-left text-[12.5px]`,t.innerHTML=`<thead class="sticky top-0 bg-neutral-50 text-[10.5px] uppercase tracking-wide text-neutral-400 shadow-[inset_0_-1px_0_var(--color-neutral-200)]"><tr>
1
+ import{A as e,B as t,L as n,P as r,R as i,U as a,V as o,h as s,q as c,w as l,y as u}from"./index-DzWnvhXb.js";var d=(e,t={})=>{let n=document.createElementNS(`http://www.w3.org/2000/svg`,e);for(let[e,r]of Object.entries(t))n.setAttribute(e,r);return n},f=e=>e===null?`-`:n(Date.now()-e),p={light:{edge:`#a3a3a3`,callback:`#0891b2`,message:`#d97706`,scheduler:{fill:`#f5f3ff`,stroke:`#a78bfa`,text:`#5b21b6`},console:{fill:`#eff6ff`,stroke:`#93c5fd`,text:`#1d4ed8`},task:{fill:`#fffbeb`,stroke:`#fbbf24`,text:`#92400e`},process:{fill:`#f5f5f5`,stroke:`#a3a3a3`,text:`#525252`,dash:`4 3`},streaming:{fill:`#ffffff`,stroke:`#10b981`,text:`#262626`},idle:{fill:`#fafafa`,stroke:`#d4d4d4`,text:`#737373`}},dark:{edge:`#7a7a7a`,callback:`#3fc3dd`,message:`#e0a13a`,scheduler:{fill:`#2b2440`,stroke:`#7d63c9`,text:`#c9b8f5`},console:{fill:`#1f2a3d`,stroke:`#4a7fbf`,text:`#a8c8f0`},task:{fill:`#3a2f1c`,stroke:`#b8862a`,text:`#f0d49a`},process:{fill:`#262626`,stroke:`#5c5c5c`,text:`#a3a3a3`,dash:`4 3`},streaming:{fill:`#22302a`,stroke:`#10b981`,text:`#e5e5e5`},idle:{fill:`#212121`,stroke:`#4a4a4a`,text:`#9a9a9a`}}},m=()=>p[document.documentElement.dataset.theme===`dark`?`dark`:`light`];function h(n,p,h){let g=`sessions`,_=`active`,v={sessions:[],runs:[],messages:[]},y=``,b=o(async()=>{let e=_,t=await a(`/api/activity?scope=${e}`,`Failed to load activity`);if(!t.ok){y=``,n.replaceChildren(i(`p`,`p-4 text-[13px] text-red-600`,t.error));return}if(e!==_)return;let r=JSON.stringify(t.value);if(`${e}:${r}`===y)return;y=`${e}:${r}`;let o=t.value;v={...o,sessions:o.sessions.map(e=>({...e,title:c(e.title)}))},x()});function x(){let t=e(l([[`Sessions`,`sessions`],[`Relationships`,`dependencies`]],g,e=>{g=e,x()}),i(`span`,`ml-auto text-[11.5px] text-neutral-400`,`${v.sessions.length} sessions · ${v.runs.length} runs`),l([[`Current`,`active`],[`Last 24h`,`recent`]],_,e=>{_=e,x(),b()})),r=i(`div`,`min-h-0 flex-1 overflow-auto`);g===`sessions`?S(r):w(r),n.replaceChildren(t,r)}function S(e){let n=document.createElement(`table`);n.className=`w-full table-fixed text-left text-[12.5px]`,n.innerHTML=`<thead class="sticky top-0 bg-neutral-50 text-[10.5px] uppercase tracking-wide text-neutral-400 shadow-[inset_0_-1px_0_var(--color-neutral-200)]"><tr>
2
2
  <th class="w-[50%] px-4 py-2 font-semibold md:w-[34%]">Session</th>
3
3
  <th class="hidden w-[38%] px-2 py-2 font-semibold md:table-cell">Project</th>
4
4
  <th class="px-2 py-2 font-semibold md:w-[14%]">State</th>
5
- <th class="px-2 py-2 font-semibold">In state</th></tr></thead>`;let n=document.createElement(`tbody`);for(let e of v.sessions){let t=document.createElement(`tr`);t.className=`cursor-pointer border-b border-neutral-100 transition-colors hover:bg-neutral-50`,t.onclick=()=>p(e.id);let r=a(`button`,`block w-full cursor-pointer truncate text-left font-medium text-neutral-800`,e.title??u(e.cwd));r.setAttribute(`type`,`button`),r.title=`${e.id}\n${e.cwd}`,r.onclick=t=>{t.stopPropagation(),p(e.id)},t.append(a(`td`,`px-4 py-2.5`,r,a(`div`,`truncate font-mono text-[11px] text-neutral-400 md:hidden`,e.cwd||`-`)),a(`td`,`hidden truncate px-2 py-2.5 font-mono text-[11.5px] text-neutral-500 md:table-cell`,e.cwd||`-`),a(`td`,`px-2 py-2.5`,C(e.state)),a(`td`,`px-2 py-2.5 font-mono text-[11.5px] text-neutral-400`,f(e.stateSince))),n.append(t)}t.append(n),e.append(t),v.sessions.length||e.append(a(`div`,`p-4`,l(_===`active`?`No active sessions.`:`No sessions in the last 24 hours.`)))}function C(e){return e===`streaming`?c(e,`bg-emerald-50 text-emerald-700 ring-emerald-200`,`animate-pulse bg-emerald-500`):c(e,`bg-neutral-100 text-neutral-600 ring-neutral-200`,`bg-neutral-300`)}function w(e){let t=new Map,n=[],r=(e,n,r,i)=>{t.has(e)||t.set(e,{id:e,label:n,kind:r,state:i})};for(let e of v.sessions)r(e.id,e.title??e.id.slice(0,10),`session`,e.state);for(let e of v.runs){let t=e.invokedBySessionId??`scheduler`,i=e.targetSessionId??(e.context.definition.action.type===`bash`?`process:${e.id}`:`task:${e.id}`);r(t,t===`scheduler`?`Scheduler`:t.slice(0,10),t===`scheduler`?`scheduler`:`session`),r(i,e.targetSessionId?e.targetSessionId.slice(0,10):e.context.definition.action.type===`bash`?`Bash process`:`Task`,e.targetSessionId?`session`:e.context.definition.action.type===`bash`?`process`:`task`),n.push({from:t,to:i,run:e,kind:`invocation`}),e.callbackSessionId&&(r(e.callbackSessionId,e.callbackSessionId.slice(0,10),`session`),n.push({from:i,to:e.callbackSessionId,run:e,kind:`callback`}))}for(let e of v.messages){if(!e.fromSessionId||!e.toSessionId||e.fromSessionId===e.toSessionId)continue;let t=v.runs.find(t=>t.id===e.runId);if(t){for(let t of[e.fromSessionId,e.toSessionId])r(t,t===`console`?`Console`:t.slice(0,10),t===`console`?`console`:`session`);n.push({from:e.fromSessionId,to:e.toSessionId,run:t,kind:`message`})}}if(!t.size){e.append(a(`div`,`p-4`,l(_===`active`?`No active dependencies.`:`No dependencies in the last 24 hours.`)));return}let i=[...t.values()],o=new Set(n.map(e=>e.to)),s=new Map,c=i.filter(e=>!o.has(e.id)).map(e=>e.id);c.length||c.push(i[0].id);for(let e of c)s.set(e,0);for(;c.length;){let e=c.shift();for(let t of n)t.from!==e||s.has(t.to)||(s.set(t.to,s.get(e)+1),c.push(t.to))}for(let e of i)s.has(e.id)||s.set(e.id,0);let u=new Map;for(let e of i){let t=s.get(e.id);u.set(t,[...u.get(t)??[],e])}let f=Math.max(...u.keys())+1,g=Math.max(...[...u.values()].map(e=>e.length)),y=n.some(e=>e.from!==e.to&&s.get(e.from)===s.get(e.to))?40:0,b=56+f*180+(f-1)*110+y,x=56+g*46+(g-1)*30,S=new Map;for(let[e,t]of u)t.forEach((t,n)=>S.set(t.id,{x:28+e*290+90,y:28+n*76+23}));let C=d(`svg`,{width:String(b),height:String(x),viewBox:`0 0 ${b} ${x}`,class:`block`}),w=d(`defs`),T=m();for(let[e,t]of[[`activity-arrow`,T.edge],[`activity-arrow-cb`,T.callback],[`activity-arrow-msg`,T.message]]){let n=d(`marker`,{id:e,viewBox:`0 0 10 10`,refX:`9`,refY:`5`,markerWidth:`6`,markerHeight:`6`,orient:`auto-start-reverse`});n.append(d(`path`,{d:`M 0 0 L 10 5 L 0 10 z`,fill:t})),w.append(n)}C.append(w);let E=new Set;for(let e of n){let t=`${e.from}→${e.to}:${e.kind}`;if(E.has(t))continue;E.add(t);let n=S.get(e.from),r=S.get(e.to),i=d(`path`,{d:n.x===r.x?`M ${n.x+90} ${n.y} C ${n.x+90+46} ${n.y}, ${r.x+90+46} ${r.y}, ${r.x+90} ${r.y}`:(()=>{let e=r.x>n.x,t=n.x+(e?90:-90),i=r.x+(e?-90:90),a=Math.max(36,Math.abs(i-t)*.45)*(e?1:-1);return`M ${t} ${n.y} C ${t+a} ${n.y}, ${i-a} ${r.y}, ${i} ${r.y}`})(),fill:`none`,stroke:e.kind===`callback`?T.callback:e.kind===`message`?T.message:T.edge,"stroke-width":`1.5`,"marker-end":e.kind===`callback`?`url(#activity-arrow-cb)`:e.kind===`message`?`url(#activity-arrow-msg)`:`url(#activity-arrow)`});e.kind===`callback`&&i.setAttribute(`stroke-dasharray`,`6 5`),e.kind===`message`&&i.setAttribute(`stroke-dasharray`,`2 5`),i.classList.add(`cursor-pointer`),i.onclick=()=>h(e.run.id),C.append(i)}let D=/[\u1100-\u11FF\u2E80-\uA4CF\uAC00-\uD7A3\uF900-\uFAFF\uFE30-\uFE4F\uFF00-\uFF60\uFFE0-\uFFE6]/,O=(e,t)=>{let n=0;for(let r=0;r<e.length;r++)if(n+=D.test(e[r])?2:1,n>t)return`${e.slice(0,r)}…`;return e};for(let e of i){let t=S.get(e.id),n=e.kind===`session`?e.state===`streaming`?T.streaming:T.idle:T[e.kind],r=d(`g`,{transform:`translate(${t.x},${t.y})`});e.kind===`session`&&r.classList.add(`cursor-pointer`),r.onclick=()=>{e.kind===`session`&&p(e.id)};let i=d(`rect`,{x:`-90`,y:`-23`,width:`180`,height:`46`,rx:`8`,fill:n.fill,stroke:n.stroke,"stroke-width":e.kind===`session`&&e.state===`streaming`?`1.5`:`1`});n.dash&&i.setAttribute(`stroke-dasharray`,n.dash),r.append(i);let a=-78;if(e.kind===`session`){let t=d(`circle`,{cx:`-75`,cy:`0`,r:`3.5`,fill:n.stroke});if(e.state===`streaming`){let e=d(`animate`,{attributeName:`opacity`,values:`1;0.3;1`,dur:`1.6s`,repeatCount:`indefinite`});t.append(e)}r.append(t),a=-64}let o=d(`text`,{x:String(a),"text-anchor":`start`,"dominant-baseline":`middle`,"font-size":`11.5`,"font-weight":e.kind===`session`?`500`:`400`,fill:n.text});o.textContent=O(e.label,e.kind===`session`?22:25);let s=d(`title`);s.textContent=e.label===e.id?e.id:`${e.label}\n${e.id}`,r.append(o,s),C.append(r)}let k=e=>a(`span`,`inline-block h-2 w-2 rounded-full ${e}`),A=e=>a(`span`,`inline-block h-0 w-6 border-t-2 ${e}`);e.append(a(`div`,`w-max p-4`,C),a(`div`,`flex flex-wrap gap-x-5 gap-y-1.5 border-t border-neutral-200 bg-neutral-50/60 px-4 py-2 text-[11px] text-neutral-500`,a(`span`,`inline-flex items-center gap-2`,A(`border-solid border-neutral-400`),`task invocation`),a(`span`,`inline-flex items-center gap-2 text-cyan-700`,A(`border-dashed border-cyan-500`),`callback`),a(`span`,`inline-flex items-center gap-2 text-amber-700`,A(`border-dotted border-amber-500`),`supervisor / control`),a(`span`,`inline-flex items-center gap-1.5`,k(`bg-emerald-500`),`streaming`),a(`span`,`inline-flex items-center gap-1.5`,k(`bg-neutral-300`),`idle`)))}let T=o(r,e=>{e===`dependencies`?g=`dependencies`:e===`sessions`&&(g=`sessions`),b()},()=>{r.replaceChildren(),v={sessions:[],runs:[],messages:[]},y=``});return window.addEventListener(`pier:theme`,()=>{T.visible&&x()}),Object.assign(T,{refresh(){T.visible&&b()}})}export{h as createActivityView};
5
+ <th class="px-2 py-2 font-semibold">In state</th></tr></thead>`;let r=document.createElement(`tbody`);for(let e of v.sessions){let n=document.createElement(`tr`);n.className=`cursor-pointer border-b border-neutral-100 transition-colors hover:bg-neutral-50`,n.onclick=()=>p(e.id);let a=i(`button`,`block w-full cursor-pointer truncate text-left font-medium text-neutral-800`,e.title??t(e.cwd));a.setAttribute(`type`,`button`),a.title=`${e.id}\n${e.cwd}`,a.onclick=t=>{t.stopPropagation(),p(e.id)},n.append(i(`td`,`px-4 py-2.5`,a,i(`div`,`truncate font-mono text-[11px] text-neutral-400 md:hidden`,e.cwd||`-`)),i(`td`,`hidden truncate px-2 py-2.5 font-mono text-[11.5px] text-neutral-500 md:table-cell`,e.cwd||`-`),i(`td`,`px-2 py-2.5`,C(e.state)),i(`td`,`px-2 py-2.5 font-mono text-[11.5px] text-neutral-400`,f(e.stateSince))),r.append(n)}n.append(r),e.append(n),v.sessions.length||e.append(i(`div`,`p-4`,u(_===`active`?`No active sessions.`:`No sessions in the last 24 hours.`)))}function C(e){return e===`streaming`?s(e,`bg-emerald-50 text-emerald-700 ring-emerald-200`,`animate-pulse bg-emerald-500`):s(e,`bg-neutral-100 text-neutral-600 ring-neutral-200`,`bg-neutral-300`)}function w(e){let t=new Map,n=[],r=(e,n,r,i)=>{t.has(e)||t.set(e,{id:e,label:n,kind:r,state:i})};for(let e of v.sessions)r(e.id,e.title??e.id.slice(0,10),`session`,e.state);for(let e of v.runs){let t=e.invokedBySessionId??`scheduler`,i=e.targetSessionId??(e.context.definition.action.type===`bash`?`process:${e.id}`:`task:${e.id}`);r(t,t===`scheduler`?`Scheduler`:t.slice(0,10),t===`scheduler`?`scheduler`:`session`),r(i,e.targetSessionId?e.targetSessionId.slice(0,10):e.context.definition.action.type===`bash`?`Bash process`:`Task`,e.targetSessionId?`session`:e.context.definition.action.type===`bash`?`process`:`task`),n.push({from:t,to:i,run:e,kind:`invocation`}),e.callbackSessionId&&(r(e.callbackSessionId,e.callbackSessionId.slice(0,10),`session`),n.push({from:i,to:e.callbackSessionId,run:e,kind:`callback`}))}for(let e of v.messages){if(!e.fromSessionId||!e.toSessionId||e.fromSessionId===e.toSessionId)continue;let t=v.runs.find(t=>t.id===e.runId);if(t){for(let t of[e.fromSessionId,e.toSessionId])r(t,t===`console`?`Console`:t.slice(0,10),t===`console`?`console`:`session`);n.push({from:e.fromSessionId,to:e.toSessionId,run:t,kind:`message`})}}if(!t.size){e.append(i(`div`,`p-4`,u(_===`active`?`No active dependencies.`:`No dependencies in the last 24 hours.`)));return}let a=[...t.values()],o=new Set(n.map(e=>e.to)),s=new Map,c=a.filter(e=>!o.has(e.id)).map(e=>e.id);c.length||c.push(a[0].id);for(let e of c)s.set(e,0);for(;c.length;){let e=c.shift();for(let t of n)t.from!==e||s.has(t.to)||(s.set(t.to,s.get(e)+1),c.push(t.to))}for(let e of a)s.has(e.id)||s.set(e.id,0);let l=new Map;for(let e of a){let t=s.get(e.id);l.set(t,[...l.get(t)??[],e])}let f=Math.max(...l.keys())+1,g=Math.max(...[...l.values()].map(e=>e.length)),y=n.some(e=>e.from!==e.to&&s.get(e.from)===s.get(e.to))?40:0,b=56+f*180+(f-1)*110+y,x=56+g*46+(g-1)*30,S=new Map;for(let[e,t]of l)t.forEach((t,n)=>S.set(t.id,{x:28+e*290+90,y:28+n*76+23}));let C=d(`svg`,{width:String(b),height:String(x),viewBox:`0 0 ${b} ${x}`,class:`block`}),w=d(`defs`),T=m();for(let[e,t]of[[`activity-arrow`,T.edge],[`activity-arrow-cb`,T.callback],[`activity-arrow-msg`,T.message]]){let n=d(`marker`,{id:e,viewBox:`0 0 10 10`,refX:`9`,refY:`5`,markerWidth:`6`,markerHeight:`6`,orient:`auto-start-reverse`});n.append(d(`path`,{d:`M 0 0 L 10 5 L 0 10 z`,fill:t})),w.append(n)}C.append(w);let E=new Set;for(let e of n){let t=`${e.from}→${e.to}:${e.kind}`;if(E.has(t))continue;E.add(t);let n=S.get(e.from),r=S.get(e.to),i=d(`path`,{d:n.x===r.x?`M ${n.x+90} ${n.y} C ${n.x+90+46} ${n.y}, ${r.x+90+46} ${r.y}, ${r.x+90} ${r.y}`:(()=>{let e=r.x>n.x,t=n.x+(e?90:-90),i=r.x+(e?-90:90),a=Math.max(36,Math.abs(i-t)*.45)*(e?1:-1);return`M ${t} ${n.y} C ${t+a} ${n.y}, ${i-a} ${r.y}, ${i} ${r.y}`})(),fill:`none`,stroke:e.kind===`callback`?T.callback:e.kind===`message`?T.message:T.edge,"stroke-width":`1.5`,"marker-end":e.kind===`callback`?`url(#activity-arrow-cb)`:e.kind===`message`?`url(#activity-arrow-msg)`:`url(#activity-arrow)`});e.kind===`callback`&&i.setAttribute(`stroke-dasharray`,`6 5`),e.kind===`message`&&i.setAttribute(`stroke-dasharray`,`2 5`),i.classList.add(`cursor-pointer`),i.onclick=()=>h(e.run.id),C.append(i)}let D=/[\u1100-\u11FF\u2E80-\uA4CF\uAC00-\uD7A3\uF900-\uFAFF\uFE30-\uFE4F\uFF00-\uFF60\uFFE0-\uFFE6]/,O=(e,t)=>{let n=0;for(let r=0;r<e.length;r++)if(n+=D.test(e[r])?2:1,n>t)return`${e.slice(0,r)}…`;return e};for(let e of a){let t=S.get(e.id),n=e.kind===`session`?e.state===`streaming`?T.streaming:T.idle:T[e.kind],r=d(`g`,{transform:`translate(${t.x},${t.y})`});e.kind===`session`&&r.classList.add(`cursor-pointer`),r.onclick=()=>{e.kind===`session`&&p(e.id)};let i=d(`rect`,{x:`-90`,y:`-23`,width:`180`,height:`46`,rx:`8`,fill:n.fill,stroke:n.stroke,"stroke-width":e.kind===`session`&&e.state===`streaming`?`1.5`:`1`});n.dash&&i.setAttribute(`stroke-dasharray`,n.dash),r.append(i);let a=-78;if(e.kind===`session`){let t=d(`circle`,{cx:`-75`,cy:`0`,r:`3.5`,fill:n.stroke});if(e.state===`streaming`){let e=d(`animate`,{attributeName:`opacity`,values:`1;0.3;1`,dur:`1.6s`,repeatCount:`indefinite`});t.append(e)}r.append(t),a=-64}let o=d(`text`,{x:String(a),"text-anchor":`start`,"dominant-baseline":`middle`,"font-size":`11.5`,"font-weight":e.kind===`session`?`500`:`400`,fill:n.text});o.textContent=O(e.label,e.kind===`session`?22:25);let s=d(`title`);s.textContent=e.label===e.id?e.id:`${e.label}\n${e.id}`,r.append(o,s),C.append(r)}let k=e=>i(`span`,`inline-block h-2 w-2 rounded-full ${e}`),A=e=>i(`span`,`inline-block h-0 w-6 border-t-2 ${e}`);e.append(i(`div`,`w-max p-4`,C),i(`div`,`flex flex-wrap gap-x-5 gap-y-1.5 border-t border-neutral-200 bg-neutral-50/60 px-4 py-2 text-[11px] text-neutral-500`,i(`span`,`inline-flex items-center gap-2`,A(`border-solid border-neutral-400`),`task invocation`),i(`span`,`inline-flex items-center gap-2 text-cyan-700`,A(`border-dashed border-cyan-500`),`callback`),i(`span`,`inline-flex items-center gap-2 text-amber-700`,A(`border-dotted border-amber-500`),`supervisor / control`),i(`span`,`inline-flex items-center gap-1.5`,k(`bg-emerald-500`),`streaming`),i(`span`,`inline-flex items-center gap-1.5`,k(`bg-neutral-300`),`idle`)))}let T=r(n,e=>{e===`dependencies`?g=`dependencies`:e===`sessions`&&(g=`sessions`),b()},()=>{n.replaceChildren(),v={sessions:[],runs:[],messages:[]},y=``});return window.addEventListener(`pier:theme`,()=>{T.visible&&x()}),Object.assign(T,{refresh(){T.visible&&b()}})}export{h as createActivityView};
@@ -0,0 +1 @@
1
+ import{F as e,G as t,H as n,K as r,P as i,R as a,U as o,g as s,k as c,z as l}from"./index-DzWnvhXb.js";var u=e=>e.public&&e.token?`/p/${e.slug}-${e.token}/`:`/boards/${e.slug}/`;function d(d,f){let p=[],m=``,h=a(`header`,`sticky top-0 z-30 flex h-10 items-center gap-3 border-b border-neutral-200 bg-white px-4`,a(`span`,`font-medium max-md:hidden`,`Boards`)),g=a(`div`,`px-4 py-5`);d.append(a(`div`,`min-h-0 flex-1 overflow-y-auto`,h,g));async function _(e,t){let i=await r(`/api/boards/${e.slug}`,{public:t},`PATCH`);if(i.ok)return e.token=(await i.json().catch(()=>({}))).token??``,x();m=await n(i,`Could not change ${e.slug}`),await S()}async function v(e){m=await t(`/api/boards/${e}`,`DELETE`,`Could not delete ${e}`)??``,await S()}function y(t){let n=a(`div`,`group flex flex-col gap-1 border-b border-neutral-200/70 px-1 py-2.5 last:border-b-0`),r=document.createElement(`a`);r.className=`truncate font-medium text-neutral-800 hover:text-indigo-700`,r.href=u(t),r.target=`_blank`,r.rel=`noreferrer`,r.textContent=t.title;let i=a(`div`,`flex items-center gap-2`,r,a(`span`,`flex-none font-mono text-[11.5px] text-neutral-400`,t.slug)),o=c(``,``,t.public,e=>{t.public=e,_(t,e)});o.className=`ml-auto flex flex-none cursor-pointer items-center gap-1.5 text-[11.5px] text-neutral-500`,o.append(a(`span`,``,`Public`));let d=s(`Delete`,`flex-none cursor-pointer text-[11.5px] text-neutral-400 opacity-0 transition-opacity hover:text-red-600 group-hover:opacity-100`);d.title=`Renames the folder on disk; nothing is erased`,d.onclick=()=>void v(t.slug);let p=e(`flex-none cursor-pointer text-[11.5px] text-neutral-400 opacity-0 transition-opacity hover:text-indigo-600 group-hover:opacity-100`,()=>`${location.origin}${u(t)}`);p.title=`Copy the board's link`,i.append(o,p,d),n.append(i);let m=a(`div`,`flex flex-wrap items-center gap-x-3 gap-y-1 text-[11.5px] text-neutral-500`);t.description&&m.append(a(`span`,`min-w-0 truncate`,t.description)),m.append(a(`span`,`flex-none text-neutral-400`,l(Date.parse(t.updatedAt))));for(let e of t.sessions){let t=s(e.slice(0,8),`flex-none cursor-pointer font-mono text-indigo-600 hover:underline`);t.title=`Open session ${e}`,t.onclick=()=>f(e),m.append(t)}if(t.public&&t.token){let e=a(`span`,`flex-none font-mono text-neutral-400`,u(t));e.title=`Public URL — anyone holding this link can read the board, no password`,m.append(e)}return n.append(m),n}let b=[{title:`Public`,hint:`Readable at /p/<slug>-<token>/ by anyone holding the link.`,wanted:!0},{title:`Private`,hint:`Reachable from the Console only.`,wanted:!1}];function x(){let e=a(`div`,`mx-auto flex max-w-3xl flex-col`);p.length===0&&e.append(a(`p`,`rounded-xl border border-dashed border-neutral-300 px-4 py-6 text-center text-[12.5px] text-neutral-500`,`No boards yet. Ask an agent to build one — it writes a folder under ~/.pier/boards.`));for(let{title:t,hint:n,wanted:r}of b){let i=p.filter(e=>e.public===r);if(i.length===0)continue;let o=a(`section`,`mb-6`);o.append(a(`h2`,`text-[13px] font-semibold text-neutral-700`,t)),o.append(a(`p`,`mb-1 text-[11.5px] leading-snug text-neutral-500`,n));let s=a(`div`,`rounded-xl border border-neutral-200 bg-white px-3`);s.append(...i.map(y)),o.append(s),e.append(o)}g.replaceChildren(...m?[a(`p`,`mb-3 text-[13px] text-red-600`,m)]:[],e)}async function S(){let e=await o(`/api/boards`,`Could not load boards`);if(!e.ok)return m=e.error,x();p=e.value,x(),m=``}let C=i(d,()=>void S());return window.addEventListener(`focus`,()=>{C.visible&&S()}),C}export{d as createBoardsView};
@@ -0,0 +1,4 @@
1
+ import{I as e,N as t,P as n,R as r,W as i,a,d as o,f as s,i as c,n as l,s as u,u as d,z as f}from"./index-DzWnvhXb.js";var p=e=>{let t=new Date(e),n=e=>String(e).padStart(2,`0`);return`${t.getFullYear()}-${n(t.getMonth()+1)}-${n(t.getDate())} ${n(t.getHours())}:${n(t.getMinutes())}:${n(t.getSeconds())}`},m=e=>[e.subject,e.body,[[e.author,e.email?`<${e.email}>`:``].filter(Boolean).join(` `),e.at?p(e.at):``].filter(Boolean).join(` · `)].filter(Boolean).join(`
2
+
3
+ `),h=null;function g(){h&&(h.remove(),h=null,document.removeEventListener(`pointerdown`,g,!0),window.removeEventListener(`scroll`,g,!0))}function ee(e,t){e.onmouseenter=()=>{g();let n=t();if(!n)return;h=r(`div`,`pointer-events-none fixed z-50 max-w-96 whitespace-pre-wrap rounded-lg bg-neutral-800/95 px-3 py-2 font-sans text-[11.5px] leading-snug text-neutral-100 shadow-lg`),h.textContent=n,document.body.append(h),document.addEventListener(`pointerdown`,g,!0),window.addEventListener(`scroll`,g,!0);let i=e.getBoundingClientRect();h.style.left=`${Math.max(8,Math.min(i.left,window.innerWidth-h.offsetWidth-8))}px`,h.style.top=i.bottom+6+h.offsetHeight>window.innerHeight?`${i.top-h.offsetHeight-6}px`:`${i.bottom+6}px`},e.onmouseleave=g}function _(e,t,n){let i=r(`button`,`flex w-full cursor-pointer items-center gap-1.5 px-2 py-1 text-left hover:bg-neutral-100`,r(`span`,`w-3 flex-none text-indigo-600`,e.value===t?`✓`:``),r(`span`,`flex-none font-mono text-[11.5px] text-neutral-800`,e.label??e.value));return e.subject&&i.append(r(`span`,`min-w-0 truncate text-[11px] text-neutral-400`,e.subject)),e.author&&i.append(r(`span`,`ml-auto flex-none pl-1.5 text-[10.5px] text-neutral-400`,`${e.author} · ${e.at?f(e.at):``}`)),ee(i,()=>m(e)),i.onclick=()=>n(e.value),i}function te(e,t,n,i,o){let{base:s,head:l}=n,u=()=>l&&s===`${l}~1`?l:``,d=u()?`commit`:`compare`,f=t.commits[0],p=e=>[...e?[{value:``,label:`Working tree`,subject:`the files on disk, uncommitted changes included`}]:[],{...f,value:`HEAD`},...t.refs.map(e=>({value:e.name,subject:e.subject})),...t.commits.map(e=>({...e,value:e.hash}))],m=r(`div`,`flex w-[38rem] max-w-full flex-col`);function h(){let e=(e,t)=>{let n=r(`button`,`cursor-pointer rounded-full px-2.5 py-0.5 text-[11.5px] ${d===t?`bg-indigo-50 font-medium text-indigo-700`:`text-neutral-500 hover:bg-neutral-100`}`,e);return n.onclick=()=>{d=t,h()},n},n=(e,t,n,i)=>r(`div`,`flex min-w-0 flex-1 flex-col`,r(`div`,`flex-none px-2 pb-0.5 text-[10.5px] font-semibold uppercase tracking-wide text-neutral-400`,e),r(`div`,`min-h-0 flex-1 overflow-y-auto`,...t.map(e=>_(e,n,i)))),a=d===`compare`?r(`div`,`flex h-72 min-h-0 divide-x divide-neutral-200 pb-1`,n(`base`,p(!1),s,e=>{s=e,h(),i(s,l)}),n(`to`,p(!0),l,e=>{l=e,h(),i(s,l)})):r(`div`,`flex h-72 min-h-0 flex-col overflow-y-auto pb-1`,...t.commits.map(e=>_({value:e.hash,...e},u(),e=>{s=`${e}~1`,l=e,c(),i(s,l)}))),f=r(`div`,`flex-none border-t border-neutral-200`,_({value:o,label:`↺ ${o} ↔ Working tree`,subject:`back to the default: the main line's tip vs the files on disk`},s===o&&l===``?o:``,()=>{s=o,l=``,h(),i(s,l)}));m.replaceChildren(r(`div`,`flex flex-none items-center gap-1 border-b border-neutral-200 px-2 py-1.5`,e(`Compare two points`,`compare`),e(`One commit`,`commit`)),a,...d===`compare`?[f]:[])}h(),a(e,m)}var ne=/\.(png|jpe?g|gif|webp|avif|bmp|svg)$/i,v={A:`text-emerald-600`,M:`text-amber-600`,D:`text-red-600`},re=30,y=`pier.filesPrefs`,b=50,x=()=>{try{let e=JSON.parse(localStorage.getItem(y)??`{}`);return e&&typeof e==`object`?e:{}}catch{return{}}},S=e=>{let t=e?x()[e]:void 0;return typeof t?.cwd==`string`&&typeof t.base==`string`&&typeof t.head==`string`?{cwd:t.cwd,base:t.base,head:t.head}:null},ie=(e,t)=>{let n=x();delete n[e],n[e]=t;let r=Object.keys(n);for(let e of r.slice(0,Math.max(0,r.length-b)))delete n[e];try{localStorage.setItem(y,JSON.stringify(n))}catch(e){console.warn(`Files: could not remember this folder`,e)}},ae=(e,t=`indigo`)=>{let n=r(`button`,`min-w-0 cursor-pointer truncate rounded-md px-2 py-0.5 text-left font-mono text-[11.5px] ${t===`indigo`?`bg-indigo-50 font-medium text-indigo-700 hover:bg-indigo-100`:`bg-neutral-100 text-neutral-600 hover:bg-neutral-200`}`,e);return n.type=`button`,n};function C(a,c,f,p){let h=``,g=``,_={branch:null,refs:[],commits:[],worktrees:[]},y=`HEAD`,b=``,x=new Map,C=!0,w=!1,T=[],E=new Set,D=null,O=null,k=r(`header`,`flex h-10 flex-none items-center gap-2 border-b border-neutral-200 bg-white px-3`),A=r(`div`,`flex-none border-b border-neutral-200`),j=r(`div`,`min-h-0 flex-1 overflow-y-auto py-1`),oe=r(`aside`,`flex w-64 flex-none flex-col border-r border-neutral-200 text-[12.5px] max-md:h-2/5 max-md:w-full max-md:border-b max-md:border-r-0`,A,j),M=r(`div`,`h-full min-w-0 overflow-auto`),N=r(`div`,`pointer-events-none absolute inset-y-0 right-0 z-20 hidden w-2`),se=r(`section`,`relative min-h-0 min-w-0 flex-1`,M,N);a.append(k,r(`div`,`flex min-h-0 flex-1 max-md:flex-col`,oe,se));let P=(e,t)=>`/api/explorer/${e}?${new URLSearchParams({root:h,...t})}`,F=(e,t)=>`/api/fs/${e}?${new URLSearchParams({root:h,...t})}`,I=(e,t=`text-neutral-400`)=>r(`p`,`px-4 py-3 text-[12.5px] ${t}`,e),L=e=>{let t=0;for(let n of x.keys())n.startsWith(`${e}/`)&&t++;return t};function R(e,t){let n=new Set(t.map(e=>e.name)),r=e?`${e}/`:``;for(let[e,i]of x){if(i.status!==`D`||!e.startsWith(r))continue;let a=e.slice(r.length).split(`/`)[0];n.has(a)||(n.add(a),t.push({name:a,dir:e.slice(r.length).includes(`/`)}))}let i=t;return C&&_.branch&&(i=t.filter(e=>e.dir?L(`${r}${e.name}`)>0:x.has(`${r}${e.name}`))),i.sort((e,t)=>Number(t.dir)-Number(e.dir)||e.name.localeCompare(t.name)),i.map(e=>e.dir?ce(`${r}${e.name}`,e.name):le(`${r}${e.name}`,e.name))}async function z(e,t){try{let{entries:n}=await i(F(`ls`,{path:t}),`could not list this folder`);e.replaceChildren(...R(t,n)),e.childElementCount||e.append(I(C?`No changes.`:`Empty.`))}catch(n){let r=R(t,[]);r.length?e.replaceChildren(...r):e.replaceChildren(I(String(n),`text-red-600`))}}function ce(t,n){let i=L(t),a=[r(`span`,`truncate`,n)];i&&a.push(r(`span`,`ml-auto flex-none rounded bg-amber-100/80 px-1 font-mono text-[10px] text-amber-700`,String(i)));let{el:o,summary:s}=e(``,a);s.className+=` px-2 py-0.5 hover:bg-neutral-100`;let c=r(`div`,`pl-3`);o.append(c);let l=!1,u=()=>{l||(l=!0,c.append(I(`…`)),z(c,t))};return o.ontoggle=()=>{E[o.open?`add`:`delete`](t),o.open&&u()},(C&&_.branch&&!w||E.has(t))&&(o.open=!0,u()),o}function B(e){O?.classList.remove(`bg-indigo-50`,`text-indigo-700`),O=e,e.classList.add(`bg-indigo-50`,`text-indigo-700`)}function le(e,t){let n=x.get(e)?.status,i=r(`button`,`flex w-full cursor-pointer items-center gap-1.5 px-2 py-0.5 pl-6 text-left hover:bg-neutral-100`,r(`span`,`truncate`,t));return n&&i.append(r(`span`,`ml-auto flex-none font-mono text-[10.5px] font-semibold ${v[n]??`text-neutral-500`}`,n)),i.title=e,e===D&&B(i),i.onclick=()=>{B(i),D=e,de(e)},i}let V=()=>void z(j,``),H=(e,t)=>[...e?[r(`span`,`flex-none font-mono text-[10.5px] font-semibold text-emerald-600`,`+${e}`)]:[],...t?[r(`span`,`flex-none font-mono text-[10.5px] font-semibold text-red-600`,`−${t}`)]:[]];function U(e,n,i){let a=x.get(e),o=r(`div`,`sticky top-0 z-10 flex items-center gap-2 border-b border-neutral-200 bg-white px-4 py-1.5 font-mono text-[11.5px] text-neutral-500`,r(`span`,`truncate`,e));a&&(o.append(r(`span`,`flex-none font-semibold ${v[a.status]??``}`,a.status)),o.append(...H(a.add,a.del)));let s=r(`div`,`ml-auto flex flex-none items-center gap-2`);if(i&&s.append(i),n){let n=document.createElement(`a`);n.className=`flex-none text-neutral-400 hover:text-indigo-700 hover:underline`,n.href=F(`file`,{path:e}),n.download=t(e),n.textContent=`Download`,s.append(n)}return o.append(s),o}function ue(e){let t=0;for(;t<e.length;){if(e[t].tone!==`del`){t++;continue}let n=t;for(;t<e.length&&e[t].tone===`del`;)t++;let r=t;for(;t<e.length&&e[t].tone===`add`;)t++;let i=Math.min(r-n,t-r);for(let t=0;t<i;t++){let i=e[n+t],a=e[r+t],o=Math.min(i.text.length,a.text.length),s=0;for(;s<o&&i.text[s]===a.text[s];)s++;let c=0;for(;c<o-s&&i.text[i.text.length-1-c]===a.text[a.text.length-1-c];)c++;s+c!==0&&(i.mark=[s,i.text.length-c],a.mark=[s,a.text.length-c])}}}let W=ve();function G(){N.classList.add(`hidden`),N.replaceChildren(),W.set([],[])}let K=0,q=e=>e===K;function de(e){let t=++K;return x.has(e)?ye(e,t):fe(e,t)}async function fe(e,t){if(!q(t))return;G();let n=F(`file`,{path:e}),i=r(`div`,`min-w-0`);if(M.classList.remove(`flex`,`flex-col`),M.replaceChildren(U(e,!0),i),ne.test(e)){let e=r(`img`,`max-w-full p-4`);e.src=n,e.onerror=()=>i.replaceChildren(I(`Could not load image.`,`text-red-600`)),i.append(e);return}if(e.toLowerCase().endsWith(`.pdf`)){let t=document.createElement(`iframe`);t.className=`h-full w-full`,t.src=n,M.replaceChildren(U(e,!0),t),M.classList.add(`flex`,`flex-col`);return}i.append(I(`…`));let a=await fetch(n);if(!q(t))return;if(!a.ok){let{error:e}=await a.json().catch(()=>({}));i.replaceChildren(I(e??`HTTP ${a.status}`,`text-red-600`));return}if(!(a.headers.get(`content-type`)??``).startsWith(`text/plain`)){i.replaceChildren(I(`Binary file — use Download.`));return}let c=o(await a.text());if(!q(t))return;let l=await s(e);q(t)&&i.replaceChildren(d(c,l))}function pe(e){let t=[],n=0,r=0,i=!1,a=e.split(`
4
+ `);a.at(-1)===``&&a.pop();for(let e of a){let a=/^@@ -(\d+)(?:,\d+)? \+(\d+)(?:,\d+)? @@/.exec(e);if(a){i&&t.push({nums:[``,``],text:`⋯`,tone:``}),[n,r]=[Number(a[1]),Number(a[2])],i=!0;continue}!i||e.startsWith(`\\`)||(e.startsWith(`+`)?t.push({nums:[``,r++],text:e.slice(1),tone:`add`}):e.startsWith(`-`)?t.push({nums:[n++,``],text:e.slice(1),tone:`del`}):t.push({nums:[n++,r++],text:e.slice(1),tone:``}))}return t}function me(e){let t=[];for(let n=0;n<e.length;n++){let r=e[n].tone;if(!r)continue;let i=t.at(-1);i&&i.end===n-1?(i.end=n,(r===`add`&&i.tone===`del`||r===`del`&&i.tone===`add`)&&(i.tone=`mixed`)):t.push({start:n,end:n,tone:r})}return t}let he={add:`bg-emerald-400`,del:`bg-red-400`,mixed:`bg-amber-400`};function ge(e,t,n){N.replaceChildren(...e.map(e=>{let i=r(`button`,`pointer-events-auto absolute w-full cursor-pointer ${he[e.tone]}`);return i.style.top=`${e.start/t*100}%`,i.style.height=`${Math.max(.4,(e.end-e.start+1)/t*100)}%`,i.title=`line ${_e(n,e.start)}`,i.onclick=()=>n[e.start].scrollIntoView({block:`center`}),i})),N.classList.remove(`hidden`)}let _e=(e,t)=>e[t]?.querySelector(`span`)?.textContent?.trim()||String(t+1);function ve(){let e=[],t=[],n=-1,i=r(`span`,`flex-none text-[10.5px] text-neutral-400`),o=r=>{e.length&&(n=(n+r+e.length)%e.length,t[e[n].start].scrollIntoView({block:`center`}),i.textContent=`${n+1}/${e.length}`)},s=()=>e.length>0&&!a.classList.contains(`hidden`),c=(e,t,n,i)=>{let a=r(`button`,`icon-btn flex-none`,e);return a.onclick=()=>o(t),u(a,i,n,()=>o(t),s),a};return{el:r(`div`,`flex flex-none items-center gap-1`,i,c(`↑`,-1,`Previous change`,[`p`,`k`]),c(`↓`,1,`Next change`,[`n`,`j`])),set(r,a){e=r,t=a,n=-1,i.textContent=`${r.length} change${r.length===1?``:`s`}`}}}async function ye(e,t){G(),M.classList.remove(`flex`,`flex-col`);let n=x.get(e)?.status!==`D`;M.replaceChildren(U(e,n),I(`…`));try{let{diff:r}=await i(P(`diff`,{base:y,head:b,file:e,context:`99999`}),`could not read this diff`);if(!q(t))return;if(!r||/^Binary files /m.test(r))return fe(e,t);let a=pe(r);ue(a);let o=await s(e);if(!q(t))return;let c=d(a,o),l=[...c.children],u=me(a);W.set(u,l),M.replaceChildren(U(e,n,W.el),c),ge(u,a.length,l)}catch(n){q(t)&&M.replaceChildren(U(e,!1),I(String(n),`text-red-600`))}}let J=()=>b&&y===`${b}~1`?b:``,be=()=>_.commits.find(e=>e.hash===J())?.subject??``,xe=()=>J()?`${J()} — ${be()||`commit`}`:`${y} ↔ ${b||`working tree`}`,Se=(e,t,n,i)=>{let a=r(`button`,`flex h-6 w-6 flex-none cursor-pointer items-center justify-center rounded-md ${n?`bg-indigo-50 text-indigo-700`:`text-neutral-400 hover:bg-neutral-100 hover:text-neutral-700`}`);return a.innerHTML=e,a.title=t,a.onclick=i,a};function Y(){let e=Se(`<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" class="h-3.5 w-3.5"><path d="M2.5 3h11l-4.25 5v4.5l-2.5 1.25V8L2.5 3z" stroke-linejoin="round"/></svg>`,`Show only files that differ`,C,()=>{C=!C,Y(),V()}),t=Se(`<svg viewBox="0 0 16 16" fill="none" stroke="currentColor" stroke-width="1.5" class="h-3.5 w-3.5"><path d="m4.5 2.5 3.5 3 3.5-3M4.5 13.5l3.5-3 3.5 3" stroke-linecap="round" stroke-linejoin="round"/></svg>`,w?`Restore folders`:`Collapse all folders`,w,()=>{if(w=!w,w)T=[...E],E.clear();else for(let e of T)E.add(e);Y(),V()});if(!_.branch){A.replaceChildren(r(`div`,`flex items-center justify-end gap-1 p-1.5`,t));return}let n=ae(xe());if(J()){let e=_.commits.find(e=>e.hash===J());ee(n,()=>e?m(e):``)}else n.title=`Choose what to diff`;n.onclick=async()=>{await Te(),te(n,_,{base:y,head:b},(e,t)=>{y=e,b=t,Z()},$())};let[i,a]=[0,0];for(let e of x.values())i+=e.add,a+=e.del;A.replaceChildren(r(`div`,`flex flex-col gap-1 p-2`,n,r(`div`,`flex items-center gap-1.5 pl-1`,r(`span`,`text-[11px] text-neutral-400`,`${x.size} changed file${x.size===1?``:`s`}`),...H(i,a),r(`span`,`ml-auto`),e,t)))}let Ce=()=>{g&&h&&ie(g,{cwd:h,base:y,head:b})},X=e=>{let t=e.replace(/~\d+$/,``);return!t||t===`HEAD`||_.refs.some(e=>e.name===t)||_.commits.some(e=>e.hash===t)};async function we(){if(x=new Map,!_.branch)return;let{files:e}=await i(P(`diff`,{base:y,head:b}),`could not read what changed`);for(let t of e)x.set(t.path,{status:t.status,add:t.add,del:t.del});if(x.size<=re)for(let e of x.keys()){let t=e.split(`/`);for(let e=1;e<t.length;e++)E.add(t.slice(0,e).join(`/`))}}async function Z(){Ce();try{await we()}catch(e){Y(),j.replaceChildren(I(String(e),`text-red-600`));return}Y(),V(),D&&de(D)}function Q(){let e=ae(h||`Choose a folder…`,`neutral`);e.className+=` max-w-72`,e.title=`Switch folder`,e.onclick=()=>l(e,_.worktrees.map(e=>({path:e.path,...e.branch?{hint:e.branch}:{}})),h||void 0,f);let t=r(`button`,`icon-btn`,`✕`);t.type=`button`,t.title=`Close Files`,t.setAttribute(`aria-label`,`Close Files`),t.onclick=p,t.classList.add(`ml-auto`),k.replaceChildren(e,r(`span`,`truncate font-mono text-[11.5px] text-neutral-400`,_.branch?`⎇ ${_.branch}`:`no git`),t)}async function Te(){if(h)try{_=await i(P(`git`,{}),`could not read this repository`),Q()}catch(e){M.replaceChildren(I(String(e),`text-red-600`))}}let $=()=>_.refs.some(e=>e.name===`main`)?`main`:_.refs.some(e=>e.name===`master`)?`master`:`HEAD`;async function Ee(){if(Q(),D=null,O=null,E.clear(),G(),M.classList.remove(`flex`,`flex-col`),M.replaceChildren(I(`Select a file.`)),!h){A.replaceChildren(),j.replaceChildren(),M.replaceChildren(I(`No folder yet — pick one from the chip above.`));return}try{_=await i(P(`git`,{}),`could not read this repository`)}catch(e){_={branch:null,refs:[],commits:[],worktrees:[]},M.replaceChildren(I(String(e),`text-red-600`))}let e=S(g),t=e&&e.cwd===h&&X(e.base)&&X(e.head)?e:null;y=t?.base??$(),b=t?.head??``,Q(),await Z()}return n(a,e=>{let t=c(),n=t?.id??``,r=e?.startsWith(`/`)?e:(n===g?h:``)||S(n)?.cwd||t?.cwd||``;if(r===h&&n===g&&j.childElementCount){Q(),Te().then(Z);return}g=n,h=r,Ee()},()=>{G(),M.classList.remove(`flex`,`flex-col`),M.replaceChildren(I(`Select a file.`))})}export{C as createExplorerView};