@anchrd/intel-ui 0.16.1 → 0.18.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 (38) hide show
  1. package/package.json +10 -2
  2. package/src/agent/agent-calendar/agent-calendar.tsx +27 -2
  3. package/src/agent/agent-profile/agent-profile.tsx +20 -2
  4. package/src/agent/agent.tsx +27 -2
  5. package/src/board/board-calendar/board-calendar.tsx +89 -0
  6. package/src/board/board-card/board-card.tsx +106 -0
  7. package/src/board/board-data/board-data.ts +241 -0
  8. package/src/board/board-data/board-data.types.ts +63 -0
  9. package/src/board/board-detail/board-detail.tsx +629 -0
  10. package/src/board/board-gantt/board-gantt.ts +545 -0
  11. package/src/board/board-gantt/board-gantt.tsx +286 -0
  12. package/src/board/board-graph/board-graph.ts +174 -0
  13. package/src/board/board-graph/board-graph.tsx +168 -0
  14. package/src/board/board-items/board-items.ts +183 -0
  15. package/src/board/board-kanban/board-kanban.ts +137 -0
  16. package/src/board/board-kanban/board-kanban.tsx +257 -0
  17. package/src/board/board-status/board-status.ts +59 -0
  18. package/src/board/board-statuses/board-statuses.ts +63 -0
  19. package/src/board/board-statuses/board-statuses.tsx +228 -0
  20. package/src/board/board-table/board-table.ts +33 -0
  21. package/src/board/board-table/board-table.tsx +413 -0
  22. package/src/board/board-views/board-views.tsx +68 -0
  23. package/src/board/board-views/board-views.types.ts +29 -0
  24. package/src/board/board.tsx +251 -0
  25. package/src/components/ui/dropdown-menu.tsx +25 -0
  26. package/src/components/ui/item-calendar.tsx +181 -0
  27. package/src/components/ui/item-gantt.tsx +463 -0
  28. package/src/components/ui/kanban.tsx +282 -0
  29. package/src/components/ui/switch.tsx +25 -0
  30. package/src/data/intel-data-provider/intel-data-provider.ts +52 -0
  31. package/src/data/intel-data-provider/intel-data-provider.types.ts +31 -0
  32. package/src/i18n/de.json +90 -1
  33. package/src/i18n/en.json +90 -1
  34. package/src/i18n/es.json +90 -1
  35. package/src/node-table/node-table.tsx +3 -2
  36. package/src/nodes/nodes.tsx +16 -0
  37. package/src/resource-menu/resource-menu.tsx +26 -6
  38. package/src/styles.css +33 -0
package/src/i18n/en.json CHANGED
@@ -373,6 +373,7 @@
373
373
  "agent.calendarZones": "Each time is shown in the timezone of its own schedule, because that is when the agent runs. Where your own timezone differs, it is added beside it.",
374
374
  "agent.calendarYourTime": "{when} your time",
375
375
  "agent.calendarUnreadable": "None of this agent's schedules can be read as a cron expression, so nothing is planned.",
376
+ "agent.calendarPaused": "This agent is paused, so none of these runs will happen. They are what it would do once it is resumed.",
376
377
  "agent.log": "Runs",
377
378
  "agent.logEmpty": "This agent has not run yet.",
378
379
  "agent.logFailed": "The runs could not be read. The agent runtime may be unreachable from here.",
@@ -424,5 +425,93 @@
424
425
  "common.loading": "Loading…",
425
426
  "common.retry": "Try again",
426
427
  "common.close": "Close",
427
- "common.unavailable": "Intel is currently unavailable."
428
+ "common.unavailable": "Intel is currently unavailable.",
429
+ "board.summary": "{tasks} tasks in {statuses} statuses",
430
+ "board.views": "Views of this board",
431
+ "board.view.kanban": "Board",
432
+ "board.view.table": "Table",
433
+ "board.view.calendar": "Calendar",
434
+ "board.view.graph": "Graph",
435
+ "board.view.gantt": "Gantt",
436
+ "board.statuses": "Statuses",
437
+ "board.statusLabel": "Status name",
438
+ "board.statusUp": "Move status up",
439
+ "board.statusDown": "Move status down",
440
+ "board.statusRemove": "Remove the status {label}",
441
+ "board.addStatus": "New status",
442
+ "board.shelfFixed": "Always the last one",
443
+ "board.showArchived": "Show archived",
444
+ "board.addTask": "New task",
445
+ "board.empty": "Nothing on this board yet. Add the first task above.",
446
+ "board.noTasks": "Nothing here that matches.",
447
+ "board.laneEmpty": "Empty",
448
+ "board.archivedEmpty": "Nothing archived.",
449
+ "board.blocked": "Blocked",
450
+ "board.open": "Open",
451
+ "board.subtaskCount": "{done}/{total} subtasks",
452
+ "board.unassigned": "Unassigned",
453
+ "board.assignee.me": "Me",
454
+ "board.assignee.unnamed": "Assigned",
455
+ "board.unknownTask": "Task not available to you",
456
+ "board.column.title": "Title",
457
+ "board.column.status": "Status",
458
+ "board.column.assignee": "Assignee",
459
+ "board.column.startDate": "Start date",
460
+ "board.column.dueDate": "Due date",
461
+ "board.column.labels": "Labels",
462
+ "board.columns": "Columns",
463
+ "board.filterTitle": "Filter by title",
464
+ "board.groupBy": "Group by",
465
+ "board.groupBy.none": "Nothing",
466
+ "board.groupBy.status": "Status",
467
+ "board.groupBy.assignee": "Assignee",
468
+ "board.toggleSubtasks": "Show or hide the subtasks of {task}",
469
+ "board.calendar.previous": "Previous month",
470
+ "board.calendar.next": "Next month",
471
+ "board.calendar.today": "Today",
472
+ "board.calendar.more": "+{count} more",
473
+ "board.calendar.undated": "{count} without a due date — not in this grid, but on the board and in the table.",
474
+ "board.gantt.scale": "Scale",
475
+ "board.gantt.scale.days": "Days",
476
+ "board.gantt.scale.weeks": "Weeks",
477
+ "board.gantt.scale.months": "Months",
478
+ "board.gantt.today": "Today",
479
+ "board.gantt.move": "Move {task}",
480
+ "board.gantt.resizeStart": "Change when {task} starts",
481
+ "board.gantt.resizeEnd": "Change when {task} is due",
482
+ "board.gantt.spanLabel": "{start} to {end}",
483
+ "board.gantt.waitsFor": "waits for {tasks}",
484
+ "board.gantt.hiddenLinks": "{count} dependencies are not drawn: the task waited for has no bar here.",
485
+ "board.gantt.undated": "Without dates",
486
+ "board.gantt.undatedCount": "{count} without dates — on the board, but on no timeline.",
487
+ "board.gantt.nothingDated": "Nothing on this board has a date yet. Give a task a due date and it appears here.",
488
+ "board.graph.subtree": "Subtree",
489
+ "board.graph.wholeBoard": "Whole board",
490
+ "board.graph.parentEdge": "contains",
491
+ "board.graph.dependsEdge": "waits for",
492
+ "board.detail": "Task",
493
+ "board.description": "Description",
494
+ "board.subtasks": "Subtasks",
495
+ "board.addSubtask": "New subtask",
496
+ "board.dependencies": "Waits for",
497
+ "board.addDependency": "Add a dependency",
498
+ "board.removeDependency": "Remove this dependency",
499
+ "board.references": "References",
500
+ "board.addReference": "Add a reference",
501
+ "board.removeReference": "Remove this reference",
502
+ "board.addLabel": "New label",
503
+ "board.removeLabel": "Remove the label {label}",
504
+ "board.deleteTask": "Delete task",
505
+ "board.deleteConfirm": "Deleting “{task}” removes it and everything below it: {count} in total. Any dependency pointing into that is cleared.",
506
+ "board.deleted": "Deleted: {count}.",
507
+ "board.archivedHint": "This task is on the archive shelf. Move it to another status to bring it back.",
508
+ "board.dnd.start": "Picked up {task}",
509
+ "board.dnd.over": "{task} is over {lane}",
510
+ "board.dnd.end": "Dropped {task} into {lane}",
511
+ "board.dnd.endUnchanged": "{task} was not moved and is still in {lane}",
512
+ "board.dnd.endRefused": "{lane} no longer takes cards, {task} was not moved and is still in {from}",
513
+ "board.dnd.cancel": "Dropping {task} was cancelled",
514
+ "board.dnd.instructions": "Press enter to open this task. To move it instead, press the space bar on it and use the arrow keys to take it to another position or lane, then press space, enter or tab to put it down, or escape to leave it where it was.",
515
+ "board.card.roleDescription": "task, movable",
516
+ "board.statusTerminal": "Means done"
428
517
  }
package/src/i18n/es.json CHANGED
@@ -373,6 +373,7 @@
373
373
  "agent.calendarZones": "Cada hora se muestra en la zona horaria de su propio horario, porque es cuando se ejecuta el agente. Si tu propia zona horaria difiere, se añade al lado.",
374
374
  "agent.calendarYourTime": "{when} en tu hora",
375
375
  "agent.calendarUnreadable": "Ninguno de los horarios de este agente se puede leer como expresión cron, así que no hay nada planificado.",
376
+ "agent.calendarPaused": "Este agente está en pausa, así que ninguna de estas ejecuciones ocurrirá. Muestran lo que haría al reanudarlo.",
376
377
  "agent.log": "Ejecuciones",
377
378
  "agent.logEmpty": "Este agente todavía no se ha ejecutado.",
378
379
  "agent.logFailed": "Las ejecuciones no se han podido leer. Puede que el entorno de ejecución de agentes no sea accesible desde aquí.",
@@ -424,5 +425,93 @@
424
425
  "common.loading": "Cargando…",
425
426
  "common.retry": "Intentarlo de nuevo",
426
427
  "common.close": "Cerrar",
427
- "common.unavailable": "Intel no está disponible en este momento."
428
+ "common.unavailable": "Intel no está disponible en este momento.",
429
+ "board.summary": "{tasks} tareas en {statuses} estados",
430
+ "board.views": "Vistas de este tablero",
431
+ "board.view.kanban": "Tablero",
432
+ "board.view.table": "Tabla",
433
+ "board.view.calendar": "Calendario",
434
+ "board.view.graph": "Grafo",
435
+ "board.view.gantt": "Gantt",
436
+ "board.statuses": "Estados",
437
+ "board.statusLabel": "Nombre del estado",
438
+ "board.statusUp": "Subir el estado",
439
+ "board.statusDown": "Bajar el estado",
440
+ "board.statusRemove": "Quitar el estado {label}",
441
+ "board.addStatus": "Nuevo estado",
442
+ "board.shelfFixed": "Siempre el último",
443
+ "board.showArchived": "Mostrar archivadas",
444
+ "board.addTask": "Nueva tarea",
445
+ "board.empty": "Todavía no hay nada en este tablero. Añade arriba la primera tarea.",
446
+ "board.noTasks": "Aquí no coincide nada.",
447
+ "board.laneEmpty": "Vacío",
448
+ "board.archivedEmpty": "Nada archivado.",
449
+ "board.blocked": "Bloqueada",
450
+ "board.open": "Abierta",
451
+ "board.subtaskCount": "{done}/{total} subtareas",
452
+ "board.unassigned": "Sin asignar",
453
+ "board.assignee.me": "Yo",
454
+ "board.assignee.unnamed": "Asignada",
455
+ "board.unknownTask": "Tarea no disponible para ti",
456
+ "board.column.title": "Título",
457
+ "board.column.status": "Estado",
458
+ "board.column.assignee": "Responsable",
459
+ "board.column.startDate": "Fecha de inicio",
460
+ "board.column.dueDate": "Fecha límite",
461
+ "board.column.labels": "Etiquetas",
462
+ "board.columns": "Columnas",
463
+ "board.filterTitle": "Filtrar por título",
464
+ "board.groupBy": "Agrupar por",
465
+ "board.groupBy.none": "Nada",
466
+ "board.groupBy.status": "Estado",
467
+ "board.groupBy.assignee": "Responsable",
468
+ "board.toggleSubtasks": "Mostrar u ocultar las subtareas de {task}",
469
+ "board.calendar.previous": "Mes anterior",
470
+ "board.calendar.next": "Mes siguiente",
471
+ "board.calendar.today": "Hoy",
472
+ "board.calendar.more": "+{count} más",
473
+ "board.calendar.undated": "{count} sin fecha límite — fuera de la cuadrícula, pero en el tablero y en la tabla.",
474
+ "board.gantt.scale": "Escala",
475
+ "board.gantt.scale.days": "Días",
476
+ "board.gantt.scale.weeks": "Semanas",
477
+ "board.gantt.scale.months": "Meses",
478
+ "board.gantt.today": "Hoy",
479
+ "board.gantt.move": "Mover {task}",
480
+ "board.gantt.resizeStart": "Cambiar el inicio de {task}",
481
+ "board.gantt.resizeEnd": "Cambiar el vencimiento de {task}",
482
+ "board.gantt.spanLabel": "{start} a {end}",
483
+ "board.gantt.waitsFor": "espera a {tasks}",
484
+ "board.gantt.hiddenLinks": "{count} dependencias no se dibujan: la tarea esperada no tiene barra aquí.",
485
+ "board.gantt.undated": "Sin fechas",
486
+ "board.gantt.undatedCount": "{count} sin fechas — en el tablero, pero en ninguna línea de tiempo.",
487
+ "board.gantt.nothingDated": "Nada en este tablero tiene fecha todavía. Da a una tarea una fecha de vencimiento y aparecerá aquí.",
488
+ "board.graph.subtree": "Subárbol",
489
+ "board.graph.wholeBoard": "Todo el tablero",
490
+ "board.graph.parentEdge": "contiene",
491
+ "board.graph.dependsEdge": "espera a",
492
+ "board.detail": "Tarea",
493
+ "board.description": "Descripción",
494
+ "board.subtasks": "Subtareas",
495
+ "board.addSubtask": "Nueva subtarea",
496
+ "board.dependencies": "Espera a",
497
+ "board.addDependency": "Añadir una dependencia",
498
+ "board.removeDependency": "Quitar esta dependencia",
499
+ "board.references": "Referencias",
500
+ "board.addReference": "Añadir una referencia",
501
+ "board.removeReference": "Quitar esta referencia",
502
+ "board.addLabel": "Nueva etiqueta",
503
+ "board.removeLabel": "Quitar la etiqueta {label}",
504
+ "board.deleteTask": "Eliminar la tarea",
505
+ "board.deleteConfirm": "Eliminar «{task}» borra la tarea y todo lo que hay debajo: {count} en total. Cualquier dependencia que apunte ahí se limpia.",
506
+ "board.deleted": "Eliminadas: {count}.",
507
+ "board.archivedHint": "Esta tarea está en el archivo. Muévela a otro estado para recuperarla.",
508
+ "board.dnd.start": "{task} tomada",
509
+ "board.dnd.over": "{task} sobre {lane}",
510
+ "board.dnd.end": "{task} soltada en {lane}",
511
+ "board.dnd.endUnchanged": "{task} no se movió y sigue en {lane}",
512
+ "board.dnd.endRefused": "{lane} ya no admite tarjetas, {task} no se movió y sigue en {from}",
513
+ "board.dnd.cancel": "Se canceló soltar {task}",
514
+ "board.dnd.instructions": "Pulsa intro para abrir esta tarea. Para moverla, pulsa la barra espaciadora sobre ella y llévala con las flechas a otra posición o columna; después suéltala con espacio, intro o tabulador, o pulsa escape para dejarla donde estaba.",
515
+ "board.card.roleDescription": "tarea, movible",
516
+ "board.statusTerminal": "Cuenta como hecho"
428
517
  }
@@ -105,8 +105,9 @@ export function NodeTablePanel({ node }: { node: Node }) {
105
105
  <thead className="sticky top-0 bg-card">
106
106
  <tr>
107
107
  {table.data.columns.map((column) => (
108
- // Column names are distinct by contract (`DefineTableInput`), so the
109
- // name is the key and nothing has to fall back to a position.
108
+ // Column names are distinct on every path that writes a table — `DefineTableInput`,
109
+ // `RedefineTableInput` and, since #322, the bundle import, which used to walk past
110
+ // both — so the name is the key and nothing has to fall back to a position.
110
111
  <th
111
112
  key={column}
112
113
  scope="col"
@@ -23,6 +23,12 @@ const NodeEditor = lazy(async () => ({
23
23
  const AgentPanel = lazy(async () => ({
24
24
  default: (await import("@/agent/agent.tsx")).AgentPanel,
25
25
  }));
26
+ // Lazy for the third time, and the reason is the same: dnd-kit, TanStack Table and — behind its own
27
+ // second split — Sigma belong to the board alone, and every other screen would otherwise carry them
28
+ // into its first paint.
29
+ const BoardPanel = lazy(async () => ({
30
+ default: (await import("@/board/board.tsx")).BoardPanel,
31
+ }));
26
32
 
27
33
  export function Nodes() {
28
34
  const { data } = useIntelRouterContext();
@@ -151,6 +157,16 @@ export function Nodes() {
151
157
  <AttachmentPanel node={selected} />
152
158
  ) : selected.kind === "table" ? (
153
159
  <NodeTablePanel node={selected} />
160
+ ) : selected.kind === "board" ? (
161
+ // ⚠️ Before the editor branch for the same reason the agent is: a board's version
162
+ // body is a JSON document (#285), and BlockNote would open it as prose.
163
+ <Suspense
164
+ fallback={
165
+ <p className="p-6 text-sm text-muted-foreground">{i18n.t("common.loading")}</p>
166
+ }
167
+ >
168
+ <BoardPanel key={selected.id} node={selected} />
169
+ </Suspense>
154
170
  ) : selected.kind === "agent" ? (
155
171
  // ⚠️ Before the editor branch, and never reaching it: an agent's version body is a
156
172
  // definition document, not prose, and BlockNote would try to open JSON as a text.
@@ -357,12 +357,32 @@ export function ResourceMenu({
357
357
  <p className="text-sm">{i18n.t("flows.validateReady")}</p>
358
358
  ) : (
359
359
  <ul className="space-y-2">
360
- {validation.problems.map((problem) => (
361
- <li key={problem.code} className="rounded-md border p-3 text-sm">
362
- <span className="block font-medium">
363
- {i18n.t(`flows.problem.${problem.code}`)}
364
- </span>
365
- <span className="mt-1 block text-xs text-muted-foreground">{problem.detail}</span>
360
+ {/* ⚠️ Grouped by code, one entry per reason with every detail under it (#324).
361
+ `validateFlow` files one problem per sub-flow node, so a flow calling two
362
+ unpublished flows — the NORMAL case of that error, not its edge — produced two
363
+ entries whose head sentence is the same line of text, one under the other, and
364
+ two React children under one key.
365
+
366
+ Grouping rather than making the keys unique, because the head sentence is per CODE
367
+ (`flows.problem.<code>` is one string) and repeating it under itself says nothing
368
+ the reader did not just read. What differs is the detail, and every detail is
369
+ still here — which is the promise the comment above makes: somebody with two
370
+ missing tools should not have to ask twice. */}
371
+ {[
372
+ ...validation.problems
373
+ .reduce((byCode, problem) => {
374
+ byCode.set(problem.code, [...(byCode.get(problem.code) ?? []), problem.detail]);
375
+ return byCode;
376
+ }, new Map<string, string[]>())
377
+ .entries(),
378
+ ].map(([code, details]) => (
379
+ <li key={code} className="rounded-md border p-3 text-sm">
380
+ <span className="block font-medium">{i18n.t(`flows.problem.${code}`)}</span>
381
+ {details.map((detail) => (
382
+ <span key={detail} className="mt-1 block text-xs text-muted-foreground">
383
+ {detail}
384
+ </span>
385
+ ))}
366
386
  </li>
367
387
  ))}
368
388
  </ul>
package/src/styles.css CHANGED
@@ -39,6 +39,25 @@
39
39
  --sidebar-accent-foreground: oklch(0.205 0 0);
40
40
  --sidebar-border: oklch(0.922 0 0);
41
41
  --sidebar-ring: oklch(0.708 0 0);
42
+ /* The board's status ramp (anchrd/intel#286). These are the only tokens in Intel that carry hue
43
+ on purpose, and the reason is that they are the only ones asked to tell things APART: a month
44
+ grid of achromatic dots codes nothing, and neither does a graph whose blocked node looks like
45
+ its neighbour.
46
+
47
+ ⚠️ A step of the ramp belongs to a POSITION, never to a status id. A board's status list is
48
+ configured per board — renamed, extended, reordered (`board_configure`) — so `done` is not a
49
+ colour any more than `column four` is a name. The n-th status takes the n-th step and the ramp
50
+ wraps; `archived` is the exception and always takes `--muted-foreground`, because a shelf is
51
+ not a stage (see `board-status.ts`).
52
+
53
+ ⚠️ Colour is never the only channel. Every place these are used names the status in text as
54
+ well, so a reader who does not see the difference reads it instead. */
55
+ --board-status-1: oklch(0.58 0.02 260);
56
+ --board-status-2: oklch(0.55 0.16 255);
57
+ --board-status-3: oklch(0.65 0.15 65);
58
+ --board-status-4: oklch(0.55 0.14 150);
59
+ --board-status-5: oklch(0.53 0.18 305);
60
+ --board-status-6: oklch(0.56 0.15 20);
42
61
  }
43
62
 
44
63
  .dark {
@@ -69,6 +88,14 @@
69
88
  --sidebar-accent-foreground: oklch(0.985 0 0);
70
89
  --sidebar-border: oklch(1 0 0 / 10%);
71
90
  --sidebar-ring: oklch(0.556 0 0);
91
+ /* The same six steps, lifted for a dark surface. Same hues, same order — a board must not change
92
+ which colour a column has when the reader switches the appearance. */
93
+ --board-status-1: oklch(0.72 0.02 260);
94
+ --board-status-2: oklch(0.7 0.14 255);
95
+ --board-status-3: oklch(0.78 0.13 65);
96
+ --board-status-4: oklch(0.72 0.13 150);
97
+ --board-status-5: oklch(0.7 0.16 305);
98
+ --board-status-6: oklch(0.71 0.14 20);
72
99
  }
73
100
 
74
101
  @theme inline {
@@ -103,6 +130,12 @@
103
130
  --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
104
131
  --color-sidebar-border: var(--sidebar-border);
105
132
  --color-sidebar-ring: var(--sidebar-ring);
133
+ --color-board-status-1: var(--board-status-1);
134
+ --color-board-status-2: var(--board-status-2);
135
+ --color-board-status-3: var(--board-status-3);
136
+ --color-board-status-4: var(--board-status-4);
137
+ --color-board-status-5: var(--board-status-5);
138
+ --color-board-status-6: var(--board-status-6);
106
139
  }
107
140
 
108
141
  @layer base {