@anchrd/intel-ui 0.18.0 → 0.20.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/package.json +2 -9
  2. package/src/app/app-tree/app-tree.tsx +68 -147
  3. package/src/app/header-search/header-search.tsx +2 -1
  4. package/src/app/reindex-dialog/reindex-dialog.tsx +79 -0
  5. package/src/app/settings-dialog/settings-dialog.tsx +10 -32
  6. package/src/app/user-footer/user-footer.tsx +19 -0
  7. package/src/archive/archive.tsx +2 -1
  8. package/src/data/intel-data-provider/intel-data-provider.ts +42 -191
  9. package/src/data/intel-data-provider/intel-data-provider.types.ts +36 -120
  10. package/src/document-link/document-link.tsx +1 -1
  11. package/src/entry-picker/entry-picker.tsx +7 -7
  12. package/src/flow-runs/flow-runs.tsx +1 -1
  13. package/src/flows/flows.tsx +3 -2
  14. package/src/flows/node-icon/node-icon.ts +1 -1
  15. package/src/flows/node-palette/node-palette.tsx +1 -1
  16. package/src/flows/node-palette/node-palette.types.ts +1 -1
  17. package/src/graph-pane/graph-pane.tsx +1 -1
  18. package/src/i18n/de.json +14 -217
  19. package/src/i18n/en.json +14 -217
  20. package/src/i18n/es.json +14 -217
  21. package/src/import-bundle/import-bundle.tsx +152 -0
  22. package/src/kind-icon.ts +1 -15
  23. package/src/main.tsx +7 -10
  24. package/src/node-editor/node-editor.tsx +1 -1
  25. package/src/node-graph/graph-notice.tsx +1 -1
  26. package/src/node-graph/node-graph.ts +1 -1
  27. package/src/node-graph/node-graph.tsx +1 -1
  28. package/src/node-table/node-table.tsx +3 -3
  29. package/src/nodes/nodes.tsx +1 -35
  30. package/src/resource-menu/resource-menu.tsx +45 -21
  31. package/src/section-hint/section-hint.tsx +2 -2
  32. package/src/title-row/title-row.tsx +5 -22
  33. package/src/tools/tools.tsx +1 -3
  34. package/vite.config.ts +0 -4
  35. package/src/agent/agent-avatar/agent-avatar.tsx +0 -34
  36. package/src/agent/agent-calendar/agent-calendar.tsx +0 -160
  37. package/src/agent/agent-chat/agent-chat.tsx +0 -118
  38. package/src/agent/agent-costs/agent-costs.ts +0 -95
  39. package/src/agent/agent-cron/agent-cron.ts +0 -68
  40. package/src/agent/agent-definition/agent-definition.ts +0 -96
  41. package/src/agent/agent-delegation-notice/agent-delegation-notice.ts +0 -48
  42. package/src/agent/agent-entry-title/agent-entry-title.ts +0 -72
  43. package/src/agent/agent-log/agent-log.tsx +0 -308
  44. package/src/agent/agent-models/agent-models.ts +0 -166
  45. package/src/agent/agent-models/agent-models.types.ts +0 -24
  46. package/src/agent/agent-profile/agent-profile.tsx +0 -1006
  47. package/src/agent/agent-state/agent-state.ts +0 -85
  48. package/src/agent/agent-status-dot/agent-status-dot.ts +0 -73
  49. package/src/agent/agent.tsx +0 -507
  50. package/src/board/board-calendar/board-calendar.tsx +0 -89
  51. package/src/board/board-card/board-card.tsx +0 -106
  52. package/src/board/board-data/board-data.ts +0 -241
  53. package/src/board/board-data/board-data.types.ts +0 -63
  54. package/src/board/board-detail/board-detail.tsx +0 -629
  55. package/src/board/board-gantt/board-gantt.ts +0 -545
  56. package/src/board/board-gantt/board-gantt.tsx +0 -286
  57. package/src/board/board-graph/board-graph.ts +0 -174
  58. package/src/board/board-graph/board-graph.tsx +0 -168
  59. package/src/board/board-items/board-items.ts +0 -183
  60. package/src/board/board-kanban/board-kanban.ts +0 -137
  61. package/src/board/board-kanban/board-kanban.tsx +0 -257
  62. package/src/board/board-status/board-status.ts +0 -59
  63. package/src/board/board-statuses/board-statuses.ts +0 -63
  64. package/src/board/board-statuses/board-statuses.tsx +0 -228
  65. package/src/board/board-table/board-table.ts +0 -33
  66. package/src/board/board-table/board-table.tsx +0 -413
  67. package/src/board/board-views/board-views.tsx +0 -68
  68. package/src/board/board-views/board-views.types.ts +0 -29
  69. package/src/board/board.tsx +0 -251
  70. package/src/components/ui/item-calendar.tsx +0 -181
  71. package/src/components/ui/item-gantt.tsx +0 -463
  72. package/src/components/ui/kanban.tsx +0 -282
  73. package/src/data/agent-runtime/agent-runtime.ts +0 -131
  74. package/src/hooks/use-capabilities.ts +0 -22
  75. package/src/hooks/use-model-catalog.ts +0 -26
  76. package/src/timezone/timezone-combobox/timezone-combobox.tsx +0 -149
  77. package/src/timezone/timezone-context.tsx +0 -65
  78. package/src/timezone/timezone.ts +0 -174
@@ -1,63 +0,0 @@
1
- import { ArchivedBoardStatusId, type BoardStatus } from "@anchrd/intel-contract";
2
-
3
- // One row of the dialog. `terminal` travels with it because it is written in the same call as the
4
- // label and the order — `board_configure` takes the list whole (#285).
5
- export interface BoardStatusDraft {
6
- id: string;
7
- label: string;
8
- terminal: boolean;
9
- }
10
-
11
- /**
12
- * A status id out of the label somebody typed.
13
- *
14
- * ⚠️ The id is what every task in the column carries, so it is minted ONCE and never follows a
15
- * rename — renaming changes the label and no task has to be rewritten (#285). This function is
16
- * therefore only ever called for a NEW column.
17
- *
18
- * The shape is the contract's: lower case, digits and underscores, starting on an alphanumeric,
19
- * forty characters at most. A label that leaves nothing usable — one written in a script that has
20
- * no ASCII form — falls back to a generated id rather than being refused, because a column named
21
- * in Japanese is a column somebody meant to make.
22
- */
23
- export function statusIdFrom(label: string, taken: ReadonlySet<string>): string {
24
- const base = label
25
- .toLowerCase()
26
- .normalize("NFKD")
27
- // Combining marks removed, so "Prüfung" becomes "prufung" rather than losing the letter.
28
- .replace(/[\u0300-\u036f]/g, "")
29
- .replace(/[^a-z0-9]+/g, "_")
30
- .replace(/^_+|_+$/g, "")
31
- .slice(0, 40)
32
- .replace(/^[^a-z0-9]+/, "");
33
- const seed = base === "" ? "status" : base;
34
- if (!taken.has(seed) && seed !== ArchivedBoardStatusId) return seed;
35
- for (let suffix = 2; ; suffix += 1) {
36
- const candidate = `${seed.slice(0, 37)}_${suffix}`;
37
- if (!taken.has(candidate) && candidate !== ArchivedBoardStatusId) return candidate;
38
- }
39
- }
40
-
41
- /**
42
- * The list as `board_configure` takes it: whole, in the order it should be drawn.
43
- *
44
- * ⚠️ `archived` is put back at the end if the editor lost it. The contract refuses a list without
45
- * it, and a refusal here would be a dialog that cannot be saved for a reason the reader never
46
- * caused — the shelf is not a column anybody is editing.
47
- */
48
- export function configurableStatuses(statuses: BoardStatus[]): BoardStatusDraft[] {
49
- const working = statuses.filter((status) => status.id !== ArchivedBoardStatusId);
50
- const shelf = statuses.find((status) => status.id === ArchivedBoardStatusId);
51
- return [
52
- ...working.map((status) => ({
53
- id: status.id,
54
- label: status.label,
55
- terminal: status.terminal,
56
- })),
57
- // ⚠️ Always `true` for the shelf, whatever a stored board says. The contract refuses an explicit
58
- // `false` for it (anchrd/intel#311), so sending anything else would be a dialog that cannot be
59
- // saved — and a shelf that did not mean finished would hold every archived task open as a
60
- // blocker forever.
61
- { id: ArchivedBoardStatusId, label: shelf?.label ?? "Archived", terminal: true },
62
- ];
63
- }
@@ -1,228 +0,0 @@
1
- import { ArchivedBoardStatusId } from "@anchrd/intel-contract";
2
- import { ChevronDown, ChevronUp, X } from "lucide-react";
3
- import { useState } from "react";
4
- import type { BoardHandle } from "@/board/board-data/board-data.types.ts";
5
- import {
6
- Dialog,
7
- DialogContent,
8
- DialogFooter,
9
- DialogHeader,
10
- DialogTitle,
11
- } from "@/components/ui/dialog";
12
- import { useI18n } from "@/i18n/i18n-context.tsx";
13
- import { configurableStatuses, statusIdFrom } from "./board-statuses.ts";
14
-
15
- /**
16
- * The status list, edited (anchrd/intel#286, `board_configure`).
17
- *
18
- * ⚠️ The whole list is written at once and never patched — adding, renaming and reordering are one
19
- * call, because the order IS the list's order (#285). So the dialog holds a draft and sends it on
20
- * save; a control that wrote per keystroke would write a version per letter.
21
- *
22
- * ⚠️ `archived` can be renamed and cannot be removed or moved. The contract refuses a list without
23
- * it, and a shelf that could be dragged into the middle of the working columns would be a shelf
24
- * pretending to be a stage.
25
- */
26
- export function BoardStatuses({ board, onClose }: { board: BoardHandle; onClose(): void }) {
27
- const i18n = useI18n();
28
- // ⚠️ Seeded once per OPENING, which is why the caller mounts this only while it is open
29
- // (`board.tsx`) instead of handing it an `open` flag. Radix never reports `onOpenChange(true)`
30
- // for a dialog whose openness is decided outside it, so a re-seed hung off that callback would be
31
- // dead code — and the draft would then be whatever the list looked like the first time anybody
32
- // opened this board. Saving it would write that stale list WHOLE and revert a column somebody
33
- // else had added in between.
34
- const [draft, setDraft] = useState(() => configurableStatuses(board.statuses));
35
- const [added, setAdded] = useState("");
36
- // ⚠️ `board.configure` lives in `useBoard` and outlives this dialog, so its `isError` is still set
37
- // the next time the dialog is opened — a refusal from ten minutes ago greeting a list nobody has
38
- // tried to save yet. A local flag rather than `configure.reset()`, because resetting shared state
39
- // from a mount is a side effect on something this dialog does not own.
40
- const [attempted, setAttempted] = useState(false);
41
-
42
- const working = draft.filter((status) => status.id !== ArchivedBoardStatusId);
43
- const shelf = draft.find((status) => status.id === ArchivedBoardStatusId);
44
-
45
- const move = (index: number, delta: number) => {
46
- const target = index + delta;
47
- if (target < 0 || target >= working.length) return;
48
- const next = [...working];
49
- const [item] = next.splice(index, 1);
50
- if (item) next.splice(target, 0, item);
51
- setDraft([...next, ...(shelf ? [shelf] : [])]);
52
- };
53
-
54
- return (
55
- <Dialog open onOpenChange={(next) => !next && onClose()}>
56
- <DialogContent>
57
- <DialogHeader>
58
- <DialogTitle>{i18n.t("board.statuses")}</DialogTitle>
59
- </DialogHeader>
60
- <ul className="flex flex-col gap-2">
61
- {working.map((status, index) => (
62
- <li key={status.id} className="flex items-center gap-2">
63
- <input
64
- value={status.label}
65
- aria-label={i18n.t("board.statusLabel")}
66
- onChange={(event) => {
67
- const label = event.currentTarget.value;
68
- setDraft((current) =>
69
- current.map((entry) => (entry.id === status.id ? { ...entry, label } : entry)),
70
- );
71
- }}
72
- className="h-8 min-w-0 flex-1 rounded-md border bg-background px-2 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring"
73
- />
74
- {/* ⚠️ Which columns mean finished is a property of the column, not its position
75
- (anchrd/intel#311) — so it is set here, per column, and several may carry it. It is
76
- what decides whether a task waiting on this one is still blocked. */}
77
- <label className="flex shrink-0 items-center gap-1.5 text-xs text-muted-foreground">
78
- <input
79
- type="checkbox"
80
- checked={status.terminal}
81
- onChange={(event) => {
82
- const terminal = event.currentTarget.checked;
83
- setDraft((current) =>
84
- current.map((entry) =>
85
- entry.id === status.id ? { ...entry, terminal } : entry,
86
- ),
87
- );
88
- }}
89
- className="size-4 rounded border outline-none focus-visible:ring-2 focus-visible:ring-ring"
90
- />
91
- {i18n.t("board.statusTerminal")}
92
- </label>
93
- <button
94
- type="button"
95
- aria-label={i18n.t("board.statusUp")}
96
- disabled={index === 0}
97
- onClick={() => move(index, -1)}
98
- className="grid size-8 place-items-center rounded-md border outline-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-40"
99
- >
100
- <ChevronUp aria-hidden="true" className="size-4" />
101
- </button>
102
- <button
103
- type="button"
104
- aria-label={i18n.t("board.statusDown")}
105
- disabled={index === working.length - 1}
106
- onClick={() => move(index, 1)}
107
- className="grid size-8 place-items-center rounded-md border outline-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-40"
108
- >
109
- <ChevronDown aria-hidden="true" className="size-4" />
110
- </button>
111
- <button
112
- type="button"
113
- aria-label={i18n.t("board.statusRemove", { label: status.label })}
114
- // A column with tasks still in it cannot be taken away: the server refuses a task
115
- // in a status the board does not have, so removing it would make the board
116
- // unwritable rather than tidy.
117
- disabled={board.tasks.some((task) => task.status === status.id)}
118
- onClick={() =>
119
- setDraft((current) => current.filter((entry) => entry.id !== status.id))
120
- }
121
- className="grid size-8 place-items-center rounded-md border text-muted-foreground outline-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-40"
122
- >
123
- <X aria-hidden="true" className="size-4" />
124
- </button>
125
- </li>
126
- ))}
127
- {shelf ? (
128
- <li className="flex items-center gap-2">
129
- <input
130
- value={shelf.label}
131
- aria-label={i18n.t("board.statusLabel")}
132
- onChange={(event) => {
133
- const label = event.currentTarget.value;
134
- setDraft((current) =>
135
- current.map((entry) =>
136
- entry.id === ArchivedBoardStatusId ? { ...entry, label } : entry,
137
- ),
138
- );
139
- }}
140
- className="h-8 min-w-0 flex-1 rounded-md border bg-background px-2 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring"
141
- />
142
- <span className="text-xs text-muted-foreground">{i18n.t("board.shelfFixed")}</span>
143
- {/* The shelf's flag is shown and cannot be turned off: the contract refuses an explicit
144
- `false` for it, and a shelf that did not mean finished would hold every archived
145
- task open as a blocker (anchrd/intel#311). */}
146
- <label className="flex shrink-0 items-center gap-1.5 text-xs text-muted-foreground">
147
- <input
148
- type="checkbox"
149
- checked
150
- disabled
151
- aria-label={i18n.t("board.statusTerminal")}
152
- className="size-4 rounded border"
153
- />
154
- {i18n.t("board.statusTerminal")}
155
- </label>
156
- </li>
157
- ) : null}
158
- </ul>
159
- <form
160
- className="flex gap-2"
161
- onSubmit={(event) => {
162
- event.preventDefault();
163
- const label = added.trim();
164
- if (label === "") return;
165
- setDraft((current) => {
166
- const taken = new Set(current.map((entry) => entry.id));
167
- const shelfEntry = current.find((entry) => entry.id === ArchivedBoardStatusId);
168
- return [
169
- ...current.filter((entry) => entry.id !== ArchivedBoardStatusId),
170
- // A new column is work, not the end of it. Meaning "finished" is something somebody
171
- // says on purpose, with the checkbox beside it.
172
- { id: statusIdFrom(label, taken), label, terminal: false },
173
- ...(shelfEntry ? [shelfEntry] : []),
174
- ];
175
- });
176
- setAdded("");
177
- }}
178
- >
179
- <input
180
- value={added}
181
- onChange={(event) => setAdded(event.currentTarget.value)}
182
- placeholder={i18n.t("board.addStatus")}
183
- aria-label={i18n.t("board.addStatus")}
184
- className="h-8 min-w-0 flex-1 rounded-md border bg-background px-2 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring"
185
- />
186
- <button
187
- type="submit"
188
- className="h-8 rounded-md border px-3 text-sm outline-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring"
189
- >
190
- {i18n.t("common.create")}
191
- </button>
192
- </form>
193
- {attempted && board.configure.isError ? (
194
- <p role="alert" className="text-sm text-destructive">
195
- {i18n.t("node.operationFailed")}
196
- </p>
197
- ) : null}
198
- <DialogFooter>
199
- <button
200
- type="button"
201
- onClick={onClose}
202
- className="h-9 rounded-md border px-3 text-sm outline-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring"
203
- >
204
- {i18n.t("common.close")}
205
- </button>
206
- <button
207
- type="button"
208
- disabled={board.configure.isPending || draft.some((entry) => entry.label.trim() === "")}
209
- onClick={() => {
210
- setAttempted(true);
211
- board.configure.mutate(
212
- draft.map((entry) => ({
213
- id: entry.id,
214
- label: entry.label.trim(),
215
- terminal: entry.terminal,
216
- })),
217
- { onSuccess: onClose },
218
- );
219
- }}
220
- className="h-9 rounded-md bg-primary px-3 text-sm text-primary-foreground outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50"
221
- >
222
- {board.configure.isPending ? i18n.t("common.saving") : i18n.t("common.save")}
223
- </button>
224
- </DialogFooter>
225
- </DialogContent>
226
- </Dialog>
227
- );
228
- }
@@ -1,33 +0,0 @@
1
- import type { BoardTask } from "@anchrd/intel-contract";
2
- import { rootedParents } from "@/board/board-items/board-items.ts";
3
-
4
- // A task with the tasks under it, which is the shape `getSubRows` reads. `subRows` is absent rather
5
- // than empty for a leaf: TanStack treats an empty array as "expandable, with nothing in it" and
6
- // would draw a chevron that opens onto nothing.
7
- export interface BoardRow extends BoardTask {
8
- subRows?: BoardRow[];
9
- }
10
-
11
- /**
12
- * The board as the tree the table draws (anchrd/intel#286).
13
- *
14
- * ⚠️ Which task a row hangs under is `rootedParents`, shared with Gantt (anchrd/intel#293) rather
15
- * than kept here. That walk carries two subtleties — an orphan becomes a root instead of vanishing
16
- * with its hidden parent, and a cycle is broken at the task that closes it — and two copies of those
17
- * are two copies that drift, which would show as a task that is a root in one view and a child in
18
- * the other.
19
- */
20
- export function boardRows(tasks: BoardTask[]): BoardRow[] {
21
- const parents = rootedParents(tasks);
22
- const rows = new Map<string, BoardRow>(tasks.map((task) => [task.id, { ...task }]));
23
- const roots: BoardRow[] = [];
24
- for (const task of tasks) {
25
- const row = rows.get(task.id);
26
- if (!row) continue;
27
- const parent = parents.get(task.id) ?? null;
28
- const target = parent === null ? undefined : rows.get(parent);
29
- if (target === undefined) roots.push(row);
30
- else target.subRows = [...(target.subRows ?? []), row];
31
- }
32
- return roots;
33
- }