@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.
- package/package.json +2 -9
- package/src/app/app-tree/app-tree.tsx +68 -147
- package/src/app/header-search/header-search.tsx +2 -1
- package/src/app/reindex-dialog/reindex-dialog.tsx +79 -0
- package/src/app/settings-dialog/settings-dialog.tsx +10 -32
- package/src/app/user-footer/user-footer.tsx +19 -0
- package/src/archive/archive.tsx +2 -1
- package/src/data/intel-data-provider/intel-data-provider.ts +42 -191
- package/src/data/intel-data-provider/intel-data-provider.types.ts +36 -120
- package/src/document-link/document-link.tsx +1 -1
- package/src/entry-picker/entry-picker.tsx +7 -7
- package/src/flow-runs/flow-runs.tsx +1 -1
- package/src/flows/flows.tsx +3 -2
- package/src/flows/node-icon/node-icon.ts +1 -1
- package/src/flows/node-palette/node-palette.tsx +1 -1
- package/src/flows/node-palette/node-palette.types.ts +1 -1
- package/src/graph-pane/graph-pane.tsx +1 -1
- package/src/i18n/de.json +14 -217
- package/src/i18n/en.json +14 -217
- package/src/i18n/es.json +14 -217
- package/src/import-bundle/import-bundle.tsx +152 -0
- package/src/kind-icon.ts +1 -15
- package/src/main.tsx +7 -10
- package/src/node-editor/node-editor.tsx +1 -1
- package/src/node-graph/graph-notice.tsx +1 -1
- package/src/node-graph/node-graph.ts +1 -1
- package/src/node-graph/node-graph.tsx +1 -1
- package/src/node-table/node-table.tsx +3 -3
- package/src/nodes/nodes.tsx +1 -35
- package/src/resource-menu/resource-menu.tsx +45 -21
- package/src/section-hint/section-hint.tsx +2 -2
- package/src/title-row/title-row.tsx +5 -22
- package/src/tools/tools.tsx +1 -3
- package/vite.config.ts +0 -4
- package/src/agent/agent-avatar/agent-avatar.tsx +0 -34
- package/src/agent/agent-calendar/agent-calendar.tsx +0 -160
- package/src/agent/agent-chat/agent-chat.tsx +0 -118
- package/src/agent/agent-costs/agent-costs.ts +0 -95
- package/src/agent/agent-cron/agent-cron.ts +0 -68
- package/src/agent/agent-definition/agent-definition.ts +0 -96
- package/src/agent/agent-delegation-notice/agent-delegation-notice.ts +0 -48
- package/src/agent/agent-entry-title/agent-entry-title.ts +0 -72
- package/src/agent/agent-log/agent-log.tsx +0 -308
- package/src/agent/agent-models/agent-models.ts +0 -166
- package/src/agent/agent-models/agent-models.types.ts +0 -24
- package/src/agent/agent-profile/agent-profile.tsx +0 -1006
- package/src/agent/agent-state/agent-state.ts +0 -85
- package/src/agent/agent-status-dot/agent-status-dot.ts +0 -73
- package/src/agent/agent.tsx +0 -507
- package/src/board/board-calendar/board-calendar.tsx +0 -89
- package/src/board/board-card/board-card.tsx +0 -106
- package/src/board/board-data/board-data.ts +0 -241
- package/src/board/board-data/board-data.types.ts +0 -63
- package/src/board/board-detail/board-detail.tsx +0 -629
- package/src/board/board-gantt/board-gantt.ts +0 -545
- package/src/board/board-gantt/board-gantt.tsx +0 -286
- package/src/board/board-graph/board-graph.ts +0 -174
- package/src/board/board-graph/board-graph.tsx +0 -168
- package/src/board/board-items/board-items.ts +0 -183
- package/src/board/board-kanban/board-kanban.ts +0 -137
- package/src/board/board-kanban/board-kanban.tsx +0 -257
- package/src/board/board-status/board-status.ts +0 -59
- package/src/board/board-statuses/board-statuses.ts +0 -63
- package/src/board/board-statuses/board-statuses.tsx +0 -228
- package/src/board/board-table/board-table.ts +0 -33
- package/src/board/board-table/board-table.tsx +0 -413
- package/src/board/board-views/board-views.tsx +0 -68
- package/src/board/board-views/board-views.types.ts +0 -29
- package/src/board/board.tsx +0 -251
- package/src/components/ui/item-calendar.tsx +0 -181
- package/src/components/ui/item-gantt.tsx +0 -463
- package/src/components/ui/kanban.tsx +0 -282
- package/src/data/agent-runtime/agent-runtime.ts +0 -131
- package/src/hooks/use-capabilities.ts +0 -22
- package/src/hooks/use-model-catalog.ts +0 -26
- package/src/timezone/timezone-combobox/timezone-combobox.tsx +0 -149
- package/src/timezone/timezone-context.tsx +0 -65
- package/src/timezone/timezone.ts +0 -174
package/src/i18n/es.json
CHANGED
|
@@ -39,7 +39,6 @@
|
|
|
39
39
|
"tree.new.table": "Tabla nueva",
|
|
40
40
|
"tree.new.upload": "Subir un archivo",
|
|
41
41
|
"tree.new.flow": "Flujo nuevo",
|
|
42
|
-
"tree.new.agent": "Agente nuevo",
|
|
43
42
|
"tree.upload": "Archivo que subir",
|
|
44
43
|
"tree.table.columns": "Columnas",
|
|
45
44
|
"tree.table.columnsHint": "Separa los nombres de las columnas con comas. Son la cabecera, y toda fila añadida después tiene que encajar con ella.",
|
|
@@ -49,8 +48,6 @@
|
|
|
49
48
|
"tree.kind.document": "Documento",
|
|
50
49
|
"tree.kind.attachment": "Archivo",
|
|
51
50
|
"tree.kind.table": "Tabla",
|
|
52
|
-
"tree.kind.agent": "Agente",
|
|
53
|
-
"tree.kind.board": "Tablero",
|
|
54
51
|
"tree.kind.flow": "Flujo",
|
|
55
52
|
"tree.move.action": "Mover a…",
|
|
56
53
|
"tree.move.title": "Mover {title}",
|
|
@@ -76,9 +73,12 @@
|
|
|
76
73
|
"resource.rename": "Cambiar el nombre",
|
|
77
74
|
"resource.renameTitle": "Cambiar el nombre de {title}",
|
|
78
75
|
"resource.archive": "Archivar",
|
|
79
|
-
"resource.export": "
|
|
76
|
+
"resource.export": "Exportar",
|
|
77
|
+
"resource.import": "Importar",
|
|
80
78
|
"tree.new.importZip": "Importar un paquete zip",
|
|
81
79
|
"tree.new.importFolder": "Importar una carpeta",
|
|
80
|
+
"tree.importZipInto": "Importar un paquete zip en {where}",
|
|
81
|
+
"tree.importFolderInto": "Importar una carpeta en {where}",
|
|
82
82
|
"tree.importFailed": "La importación no se ha aplicado. No se ha creado nada — comprueba el paquete e inténtalo de nuevo.",
|
|
83
83
|
"resource.exportFailed": "La exportación no se ha podido descargar. Comprueba tu acceso e inténtalo de nuevo.",
|
|
84
84
|
"archive.title": "Archivo",
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
"node.attachmentHelp": "El archivo canónico se guarda en privado en Intel. Su proyección legible por la IA se indexa por separado.",
|
|
98
98
|
"node.table.summary": "{rows} filas, {columns} columnas",
|
|
99
99
|
"node.table.download": "Descargar CSV",
|
|
100
|
-
"node.table.empty": "Todavía no hay filas. Las filas las añaden los flujos
|
|
100
|
+
"node.table.empty": "Todavía no hay filas. Las filas las añaden los flujos.",
|
|
101
101
|
"node.table.undefined": "Esta tabla todavía no tiene columnas.",
|
|
102
102
|
"node.select": "Elige un documento o una carpeta para trabajar con ello.",
|
|
103
103
|
"node.folderEmpty": "En esta carpeta todavía no hay nada. Crea algo con el más de la barra lateral.",
|
|
@@ -108,8 +108,6 @@
|
|
|
108
108
|
"node.kind.document": "Documento",
|
|
109
109
|
"node.kind.attachment": "Subida",
|
|
110
110
|
"node.kind.table": "Tabla",
|
|
111
|
-
"node.kind.agent": "Agente",
|
|
112
|
-
"node.kind.board": "Tablero",
|
|
113
111
|
"node.kind.flow": "Flujo",
|
|
114
112
|
"node.share": "Compartir",
|
|
115
113
|
"node.shareAction": "Conceder acceso",
|
|
@@ -122,7 +120,6 @@
|
|
|
122
120
|
"node.verbHint.read": "Ver todo lo que hay aquí dentro",
|
|
123
121
|
"node.verbHint.write": "Cambiar y añadir cosas",
|
|
124
122
|
"node.verbHint.execute": "Iniciar los flujos que hay aquí dentro",
|
|
125
|
-
"node.verbHint.execute.agent": "Usar este agente — su chat, sus horarios y su dirección MCP",
|
|
126
123
|
"node.verbHint.share": "Dar acceso a otras personas",
|
|
127
124
|
"node.organization": "Todas las personas de la organización",
|
|
128
125
|
"node.revokeShare": "Retirar el acceso",
|
|
@@ -279,120 +276,6 @@
|
|
|
279
276
|
"runs.trigger.subflow": "llamada por otro flujo",
|
|
280
277
|
"runs.outcome.completed": "hecha",
|
|
281
278
|
"runs.outcome.failed": "fallida",
|
|
282
|
-
"agent.tabs": "Vistas del agente",
|
|
283
|
-
"agent.tab.profile": "Perfil",
|
|
284
|
-
"agent.tab.chat": "Chat",
|
|
285
|
-
"agent.tab.calendar": "Calendario",
|
|
286
|
-
"agent.tab.log": "Registro",
|
|
287
|
-
"agent.status.running": "En marcha",
|
|
288
|
-
"agent.status.waiting": "Esperando su próxima ejecución",
|
|
289
|
-
"agent.status.failed": "La última ejecución falló",
|
|
290
|
-
"agent.status.paused": "En pausa",
|
|
291
|
-
"agent.status.unknown": "Entorno de ejecución no accesible",
|
|
292
|
-
"agent.nextRun": "próxima ejecución {when} ({zone})",
|
|
293
|
-
"agent.lastRun": "última ejecución {when}",
|
|
294
|
-
"agent.pausedNoRuns": "no se dispara ningún horario, no se acepta ninguna tarea ni ningún correo; el chat sigue respondiendo",
|
|
295
|
-
"agent.pause": "Pausar",
|
|
296
|
-
"agent.resume": "Reanudar",
|
|
297
|
-
"agent.runNow": "Ejecutar ahora",
|
|
298
|
-
"agent.runNowNoTarget": "Este agente no tiene horario, así que no hay nada que ejecutar.",
|
|
299
|
-
"agent.runNowPaused": "Reanuda el agente antes de ejecutarlo.",
|
|
300
|
-
"agent.actionFailed": "Eso no ha funcionado. Inténtalo de nuevo.",
|
|
301
|
-
"agent.keyMissing": "Este agente no tiene clave de aplicación, así que no puede empezar ninguna ejecución. Sustituye su clave en «Identidad», dentro del perfil.",
|
|
302
|
-
"agent.createdBy": "por {name}",
|
|
303
|
-
"agent.notPermitted": "Puedes leer este agente, pero no manejarlo. Manejar un agente necesita el permiso «ejecutar agentes».",
|
|
304
|
-
"agent.noRuntime": "Esta instalación no tiene entorno de ejecución de agentes, así que este agente no puede chatear ni ejecutarse aquí. Su definición se conserva y vuelve a funcionar en una instalación que sí lo tenga.",
|
|
305
|
-
"agent.loadFailed": "Este agente no se ha podido cargar. Comprueba tu acceso e inténtalo de nuevo.",
|
|
306
|
-
"agent.saveFailed": "El cambio no se ha guardado. Puede que otra persona haya cambiado este agente antes — recarga e inténtalo de nuevo.",
|
|
307
|
-
"agent.noDefinition": "Este agente todavía no tiene definición, así que aquí no hay nada que cambiar.",
|
|
308
|
-
"agent.identity": "Identidad",
|
|
309
|
-
"agent.identityHint": "La aplicación de Gate con la que actúa este agente. Sus permisos en el árbol deciden qué puede leer y escribir una ejecución, y su clave vive cifrada en el entorno de ejecución de agentes — nunca aquí y nunca en la definición.",
|
|
310
|
-
"agent.principal": "Aplicación de Gate",
|
|
311
|
-
"agent.principalNone": "Ninguna",
|
|
312
|
-
"agent.principalNoneHint": "Este agente no tiene aplicación de Gate, así que no hay ninguna clave que sustituir. Los agentes creados antes de esta función, importados o restaurados desde un paquete reciben una en Gate.",
|
|
313
|
-
"agent.rotateKey": "Sustituir la clave",
|
|
314
|
-
"agent.rotateKeyConfirm": "Se emite una clave nueva y se entrega al entorno de ejecución de agentes. La clave anterior deja de funcionar de inmediato, y aquí nunca se muestra ninguna clave.",
|
|
315
|
-
"agent.rotateKeyDone": "La clave se ha sustituido. El agente funciona con la nueva a partir de su próxima ejecución.",
|
|
316
|
-
"agent.rotateKeyFailed": "La clave no se ha sustituido. Inténtalo otra vez — e insiste hasta que salga bien: si Gate ya ha emitido la clave nueva, este agente no puede ejecutarse hasta que el entorno de ejecución la tenga.",
|
|
317
|
-
"agent.contact": "Contacto",
|
|
318
|
-
"agent.contactHint": "Cómo se llega a este agente. El chat integrado forma parte del entorno de ejecución y está siempre ahí.",
|
|
319
|
-
"agent.contactChat": "Chat integrado",
|
|
320
|
-
"agent.contactChatBuiltIn": "Integrado, siempre ahí",
|
|
321
|
-
"agent.contactMcp": "Dirección MCP",
|
|
322
|
-
"agent.copy": "Copiar",
|
|
323
|
-
"agent.copied": "Copiado",
|
|
324
|
-
"agent.contactAdd": "Añadir un contacto",
|
|
325
|
-
"agent.contactAddReason": "Una dirección de correo pertenece a la instalación, no a la definición del agente, así que se configura donde está desplegado el entorno de ejecución.",
|
|
326
|
-
"agent.knows": "Lo que sabe",
|
|
327
|
-
"agent.knowsHint": "Lo que lee este agente y para qué cuenta cada entrada. Un mensaje de sistema lo instruye — una carpeta, un documento o una tabla. El contexto semántico se busca, y en la memoria se escribe de vuelta y también se busca, por eso ambas son carpetas. El nombre promete menos que el papel: lo que el agente anota en la memoria le vuelve en cada búsqueda posterior.",
|
|
328
|
-
"agent.addReference": "Añadir algo para leer",
|
|
329
|
-
"agent.removeReferenceOf": "Dejar de leer {title}",
|
|
330
|
-
"agent.pickEntry": "Qué se lee",
|
|
331
|
-
"agent.roleFor": "Para qué cuenta {title}",
|
|
332
|
-
"agent.role.system-message": "Mensaje de sistema",
|
|
333
|
-
"agent.role.semantic-context": "Contexto semántico",
|
|
334
|
-
"agent.role.memory": "Memoria",
|
|
335
|
-
"agent.unknownEntry": "No disponible para ti",
|
|
336
|
-
"agent.tools": "Herramientas",
|
|
337
|
-
"agent.toolsHint": "Los servidores MCP que le das a este agente, de entre los que tú mismo alcanzas. Intel guarda la elección, nunca el permiso — las herramientas que hay detrás de un servidor se leen en vivo con tu acceso al portal cada vez que el agente se ejecuta.",
|
|
338
|
-
"agent.addTool": "Añadir una herramienta",
|
|
339
|
-
"agent.removeToolServerOf": "Quitarle {name} a este agente",
|
|
340
|
-
"agent.toolServerUnavailable": "Ya no alcanzas este servidor",
|
|
341
|
-
"agent.toolsDelegationNotice": "Quien pueda ejecutar este agente actúa sobre tu conexión.",
|
|
342
|
-
"agent.toolsDelegationNoticeBy": "Quien pueda ejecutar este agente actúa sobre la conexión de {name}.",
|
|
343
|
-
"agent.toolsDelegationNoticeSomebody": "Quien pueda ejecutar este agente actúa sobre la conexión de quien le dio estas herramientas, no sobre la tuya.",
|
|
344
|
-
"agent.toolsNotConnected": "Todavía no has iniciado sesión en el portal de la empresa, así que no hay nada que ceder. Abre «Herramientas» para conectarte.",
|
|
345
|
-
"agent.toolsNoneLeft": "Todos los servidores que alcanzas ya están dados a este agente.",
|
|
346
|
-
"agent.schedule": "Horario",
|
|
347
|
-
"agent.scheduleHint": "Cuándo actúa este agente por su cuenta y qué ejecuta. Cada horario conserva la zona horaria con la que se creó, así que significa la misma hora para todo el que lo lea.",
|
|
348
|
-
"agent.addSchedule": "Añadir un horario",
|
|
349
|
-
"agent.removeSchedule": "Quitar este horario",
|
|
350
|
-
"agent.scheduleTarget": "Qué ejecutar",
|
|
351
|
-
"agent.cron": "Cuándo",
|
|
352
|
-
"agent.scheduleTimezone": "Zona horaria",
|
|
353
|
-
"agent.scheduleTimezoneHint": "En qué se lee la expresión de arriba. Se propone a partir de tu propia preferencia y se guarda con el horario — si cambias tu preferencia más adelante, este se queda como está.",
|
|
354
|
-
"agent.cronHint": "Cinco campos: minuto, hora, día del mes, mes, día de la semana. «0 8 * * *» es todos los días a las 08:00 en la zona horaria de abajo.",
|
|
355
|
-
"agent.model": "Modelo",
|
|
356
|
-
"agent.modelHint": "Qué modelo piensa. El entorno de ejecución recoge un cambio en menos de un minuto.",
|
|
357
|
-
"agent.modelFigures": "{context} de contexto · {input} $ de entrada / {output} $ de salida por cada millón de tokens",
|
|
358
|
-
"agent.usage": "{input} de entrada · {output} de salida · {cacheRead} leídos de caché · {cacheWrite} escritos en caché",
|
|
359
|
-
"agent.usageUnknown": "No hay cifras de consumo para esta ejecución",
|
|
360
|
-
"agent.usageNotReported": "Este proveedor no informa del consumo",
|
|
361
|
-
"agent.costMissing": "El coste no figura en el registro de la pasarela",
|
|
362
|
-
"agent.costNotConfigured": "Sin cifras de coste: esta instalación no tiene un token de lectura de AI Gateway.",
|
|
363
|
-
"agent.costUnreadable": "Sin cifras de coste: no se pudo leer el registro de AI Gateway.",
|
|
364
|
-
"agent.costPartial": "El registro de la pasarela se leyó hasta su límite de páginas, así que estos totales son un mínimo.",
|
|
365
|
-
"agent.costWindow": "Últimos {days} días",
|
|
366
|
-
"agent.costWindowCalls": "{count} llamadas al modelo",
|
|
367
|
-
"agent.costs": "Coste",
|
|
368
|
-
"agent.costsSpent": "{amount} en los últimos {days} días",
|
|
369
|
-
"agent.costsSpentPast": "Ya gastado, en ejecuciones pasadas: no es una previsión para el modelo elegido arriba.",
|
|
370
|
-
"agent.costsSpentWith": "con {models}",
|
|
371
|
-
"agent.modelFiguresStale": "Estas cifras proceden de una tabla incorporada y pueden estar desactualizadas.",
|
|
372
|
-
"agent.calendar": "Próximas ejecuciones",
|
|
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
|
-
"agent.calendarYourTime": "{when} en tu hora",
|
|
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.",
|
|
377
|
-
"agent.log": "Ejecuciones",
|
|
378
|
-
"agent.logEmpty": "Este agente todavía no se ha ejecutado.",
|
|
379
|
-
"agent.logFailed": "Las ejecuciones no se han podido leer. Puede que el entorno de ejecución de agentes no sea accesible desde aquí.",
|
|
380
|
-
"agent.runFailed": "Esta ejecución no se ha podido leer.",
|
|
381
|
-
"agent.runLog": "Lo que ha hecho esta ejecución",
|
|
382
|
-
"agent.runLogEmpty": "Esta ejecución no ha registrado nada.",
|
|
383
|
-
"agent.runAnswer": "Lo que ha respondido esta ejecución",
|
|
384
|
-
"agent.runAnswerEmpty": "Esta ejecución no ha registrado ninguna respuesta.",
|
|
385
|
-
"agent.steps": "{count} pasos",
|
|
386
|
-
"agent.trigger.chat": "desde el chat",
|
|
387
|
-
"agent.trigger.schedule": "por horario",
|
|
388
|
-
"agent.trigger.mail": "desde un correo",
|
|
389
|
-
"agent.trigger.task": "pedido por MCP",
|
|
390
|
-
"agent.trigger.manual": "iniciada a mano",
|
|
391
|
-
"agent.chatEmpty": "Escríbele a este agente. Responde con lo que sabe y con las herramientas que puede alcanzar.",
|
|
392
|
-
"agent.chatInput": "Mensaje para este agente",
|
|
393
|
-
"agent.chatPlaceholder": "Pregunta algo…",
|
|
394
|
-
"agent.chatSend": "Enviar",
|
|
395
|
-
"agent.chatScrollDown": "Saltar al mensaje más reciente",
|
|
396
279
|
"settings.title": "Preferencias",
|
|
397
280
|
"settings.description": "Idioma, apariencia y zona horaria. Las tres se aplican de inmediato y se recuerdan en este dispositivo.",
|
|
398
281
|
"settings.language": "Idioma",
|
|
@@ -400,12 +283,14 @@
|
|
|
400
283
|
"settings.appearance.system": "Seguir al sistema",
|
|
401
284
|
"settings.appearance.light": "Claro",
|
|
402
285
|
"settings.appearance.dark": "Oscuro",
|
|
403
|
-
"
|
|
404
|
-
"
|
|
405
|
-
"
|
|
406
|
-
"
|
|
407
|
-
"
|
|
408
|
-
"
|
|
286
|
+
"reindex.title": "Reconstruir el índice de búsqueda",
|
|
287
|
+
"reindex.description": "Vuelve a leer cada nodo y calcula sus entradas de búsqueda desde cero. No se pierde nada — el índice se construye a partir de los propios documentos —, pero cuesta tiempo y dinero, y después la pantalla se ve igual.",
|
|
288
|
+
"reindex.confirm": "Reconstruir",
|
|
289
|
+
"reindex.running": "Enviando…",
|
|
290
|
+
"reindex.cancel": "Cancelar",
|
|
291
|
+
"reindex.close": "Cerrar",
|
|
292
|
+
"reindex.queued": "{count} nodos están en cola. El índice se pone al día en segundo plano; mientras tanto la búsqueda sigue funcionando.",
|
|
293
|
+
"reindex.failed": "No se inició la reconstrucción. Comprueba tu acceso e inténtalo de nuevo.",
|
|
409
294
|
"auth.signOut": "Cerrar sesión",
|
|
410
295
|
"auth.signOutFailed": "El cierre de sesión ha fallado. Comprueba tu conexión e inténtalo de nuevo.",
|
|
411
296
|
"signIn.refusedTitle": "Sesión iniciada, pero no aceptada",
|
|
@@ -425,93 +310,5 @@
|
|
|
425
310
|
"common.loading": "Cargando…",
|
|
426
311
|
"common.retry": "Intentarlo de nuevo",
|
|
427
312
|
"common.close": "Cerrar",
|
|
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"
|
|
313
|
+
"common.unavailable": "Intel no está disponible en este momento."
|
|
517
314
|
}
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
import { useMutation, useQueryClient } from "@tanstack/react-query";
|
|
2
|
+
import { zipSync } from "fflate";
|
|
3
|
+
import { useRef, useState } from "react";
|
|
4
|
+
import { createPortal } from "react-dom";
|
|
5
|
+
import { treeLevelKey } from "@/app/tree-move/tree-move.tsx";
|
|
6
|
+
import { useI18n } from "@/i18n/i18n-context.tsx";
|
|
7
|
+
import { useIntelRouterContext } from "@/router/router-context.ts";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The reading half of the bundle round trip (#136/#137), as one seam two menus share.
|
|
11
|
+
*
|
|
12
|
+
* ⚠️ Import is offered in two places and they are not alternatives: the three-dot menu of a folder
|
|
13
|
+
* files into that folder, and the tree's root button is the ONLY way to reach the top level — a
|
|
14
|
+
* three-dot menu always has a resource, and the root is not one (#346). Both need the same file
|
|
15
|
+
* inputs, the same 64 MB refusal and the same invalidations, and a second copy of that is how the
|
|
16
|
+
* two start disagreeing about what a successful import makes stale.
|
|
17
|
+
*/
|
|
18
|
+
export type ImportKind = "importZip" | "importFolder";
|
|
19
|
+
|
|
20
|
+
// The server refuses anything larger anyway (#137); refusing here saves uploading it first.
|
|
21
|
+
const MaxImportBytes = 64 * 1024 * 1024;
|
|
22
|
+
|
|
23
|
+
// A picked directory as the zip the import endpoint reads. `webkitRelativePath` carries the folder
|
|
24
|
+
// structure, with the picked folder itself as the first segment — kept, so the folder lands as a
|
|
25
|
+
// folder rather than spilling its contents into the target.
|
|
26
|
+
async function zipOfDirectory(files: readonly File[]): Promise<Blob> {
|
|
27
|
+
const entries: Record<string, Uint8Array> = {};
|
|
28
|
+
let total = 0;
|
|
29
|
+
for (const file of files) {
|
|
30
|
+
const path = file.webkitRelativePath || file.name;
|
|
31
|
+
total += file.size;
|
|
32
|
+
if (total > MaxImportBytes) throw new Error("Import exceeds the 64 MB limit");
|
|
33
|
+
entries[path] = new Uint8Array(await file.arrayBuffer());
|
|
34
|
+
}
|
|
35
|
+
return new Blob([zipSync(entries).slice().buffer], { type: "application/zip" });
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export interface BundleImport {
|
|
39
|
+
/** Opens the file picker for `kind` and files whatever comes back into `parentId`. */
|
|
40
|
+
start(parentId: string | null, kind: ImportKind): void;
|
|
41
|
+
/**
|
|
42
|
+
* The two hidden file pickers. Render once inside the component that called the hook.
|
|
43
|
+
*
|
|
44
|
+
* ⚠️ They portal to `document.body` rather than landing where they are written. A file input is
|
|
45
|
+
* invisible but still an element, and the three-dot menu sits at the end of a title line whose
|
|
46
|
+
* rule is that the menu is the LAST thing in it (#53) — an input after it breaks that rule for
|
|
47
|
+
* nobody's benefit, because a hidden picker has no place on screen to be in the first place.
|
|
48
|
+
*
|
|
49
|
+
* ⚠️ `tabIndex={-1}`, and that is not a detail. The folder table renders one `ResourceMenu` PER
|
|
50
|
+
* ROW, so a folder holding twenty folders holds forty of these — and `sr-only` hides an element
|
|
51
|
+
* from the eye, never from the tab order. Without it, tabbing through such a screen walks through
|
|
52
|
+
* forty stops that show nothing, and #346 asks every entry to have a VISIBLE focus. The control
|
|
53
|
+
* that opens a picker is the menu item; the input is the mechanism behind it and is clicked by
|
|
54
|
+
* `start`, never by a person.
|
|
55
|
+
*/
|
|
56
|
+
inputs: React.ReactNode;
|
|
57
|
+
isError: boolean;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function useBundleImport(options: {
|
|
61
|
+
/**
|
|
62
|
+
* Where this pair of pickers files, in words — a folder title, or the name of the top level.
|
|
63
|
+
*
|
|
64
|
+
* ⚠️ Not decoration. Both callers render their inputs into `document.body`, so on a folder screen
|
|
65
|
+
* there are two of each in the document at once: the tree's (which files at the top level) and
|
|
66
|
+
* this menu's. `sr-only` keeps them out of sight but not out of the accessibility tree or the tab
|
|
67
|
+
* order, and two controls called "Import zip bundle" are two controls a reader cannot tell apart
|
|
68
|
+
* — nor, as the first browser run of #388 showed, can anything else looking one up by name.
|
|
69
|
+
*/
|
|
70
|
+
where: string;
|
|
71
|
+
onImported?(parentId: string | null): void | Promise<void>;
|
|
72
|
+
}): BundleImport {
|
|
73
|
+
const { data } = useIntelRouterContext();
|
|
74
|
+
const i18n = useI18n();
|
|
75
|
+
const queryClient = useQueryClient();
|
|
76
|
+
const [importTo, setImportTo] = useState<string | null>(null);
|
|
77
|
+
const zipInput = useRef<HTMLInputElement>(null);
|
|
78
|
+
const folderInput = useRef<HTMLInputElement>(null);
|
|
79
|
+
|
|
80
|
+
// One import, one request, one answer (#137): the server creates the whole subtree or nothing, so
|
|
81
|
+
// this mutation never has a partial state to clean up after — unlike the attachment upload, whose
|
|
82
|
+
// two steps can strand a node.
|
|
83
|
+
const importBundle = useMutation({
|
|
84
|
+
mutationFn: async ({ parentId, zip }: { parentId: string | null; zip: Blob }) => {
|
|
85
|
+
if (zip.size > MaxImportBytes) throw new Error("Import exceeds the 64 MB limit");
|
|
86
|
+
const result = await data.importNodeBundle({
|
|
87
|
+
nodeId: parentId,
|
|
88
|
+
zip,
|
|
89
|
+
idempotencyKey: crypto.randomUUID(),
|
|
90
|
+
});
|
|
91
|
+
return { parentId, result };
|
|
92
|
+
},
|
|
93
|
+
onSuccess: async (imported) => {
|
|
94
|
+
await options.onImported?.(imported.parentId);
|
|
95
|
+
await Promise.all([
|
|
96
|
+
queryClient.invalidateQueries({ queryKey: treeLevelKey(imported.parentId) }),
|
|
97
|
+
queryClient.invalidateQueries({ queryKey: ["node-graph"] }),
|
|
98
|
+
// The graph view of that same level draws exactly this list (#19).
|
|
99
|
+
queryClient.invalidateQueries({ queryKey: ["relation-graph"] }),
|
|
100
|
+
// A bundle can carry flows, so the flow collections go stale alongside the node ones.
|
|
101
|
+
queryClient.invalidateQueries({ queryKey: ["flows"] }),
|
|
102
|
+
]);
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
start(parentId, kind) {
|
|
108
|
+
setImportTo(parentId);
|
|
109
|
+
(kind === "importZip" ? zipInput : folderInput).current?.click();
|
|
110
|
+
},
|
|
111
|
+
isError: importBundle.isError,
|
|
112
|
+
inputs: createPortal(
|
|
113
|
+
<>
|
|
114
|
+
<input
|
|
115
|
+
ref={zipInput}
|
|
116
|
+
type="file"
|
|
117
|
+
tabIndex={-1}
|
|
118
|
+
accept=".zip,application/zip"
|
|
119
|
+
className="sr-only"
|
|
120
|
+
aria-label={i18n.t("tree.importZipInto", { where: options.where })}
|
|
121
|
+
onChange={(event) => {
|
|
122
|
+
const file = event.target.files?.[0];
|
|
123
|
+
if (file) importBundle.mutate({ parentId: importTo, zip: file });
|
|
124
|
+
event.currentTarget.value = "";
|
|
125
|
+
}}
|
|
126
|
+
/>
|
|
127
|
+
{/* `webkitdirectory` is the one way a browser hands over a folder; React does not know the
|
|
128
|
+
attribute, so it is spread past the type checker rather than invented as a prop. */}
|
|
129
|
+
<input
|
|
130
|
+
ref={folderInput}
|
|
131
|
+
type="file"
|
|
132
|
+
tabIndex={-1}
|
|
133
|
+
multiple
|
|
134
|
+
{...({ webkitdirectory: "" } as object)}
|
|
135
|
+
className="sr-only"
|
|
136
|
+
aria-label={i18n.t("tree.importFolderInto", { where: options.where })}
|
|
137
|
+
onChange={(event) => {
|
|
138
|
+
const files = Array.from(event.target.files ?? []);
|
|
139
|
+
const parentId = importTo;
|
|
140
|
+
if (files.length > 0) {
|
|
141
|
+
void zipOfDirectory(files)
|
|
142
|
+
.then((zip) => importBundle.mutate({ parentId, zip }))
|
|
143
|
+
.catch(() => importBundle.mutate({ parentId, zip: new Blob([]) }));
|
|
144
|
+
}
|
|
145
|
+
event.currentTarget.value = "";
|
|
146
|
+
}}
|
|
147
|
+
/>
|
|
148
|
+
</>,
|
|
149
|
+
document.body,
|
|
150
|
+
),
|
|
151
|
+
};
|
|
152
|
+
}
|
package/src/kind-icon.ts
CHANGED
|
@@ -1,13 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
Bot,
|
|
3
|
-
FileText,
|
|
4
|
-
Folder,
|
|
5
|
-
KanbanSquare,
|
|
6
|
-
type LucideIcon,
|
|
7
|
-
Paperclip,
|
|
8
|
-
Table,
|
|
9
|
-
Workflow,
|
|
10
|
-
} from "lucide-react";
|
|
1
|
+
import { FileText, Folder, type LucideIcon, Paperclip, Table, Workflow } from "lucide-react";
|
|
11
2
|
import type { TreeEntry } from "@/data/intel-data-provider/intel-data-provider.types.ts";
|
|
12
3
|
|
|
13
4
|
/**
|
|
@@ -24,10 +15,5 @@ export const kindIcons: Record<TreeEntry["kind"], LucideIcon> = {
|
|
|
24
15
|
document: FileText,
|
|
25
16
|
attachment: Paperclip,
|
|
26
17
|
table: Table,
|
|
27
|
-
// An agent is listed and named like any other row (#139), and since #143 opening one leads to
|
|
28
|
-
// its own screen. The sidebar's create menu draws this same icon rather than naming `Bot` again.
|
|
29
|
-
agent: Bot,
|
|
30
|
-
// A board is a row like any other (#285); the views that open one are anchrd/intel#286.
|
|
31
|
-
board: KanbanSquare,
|
|
32
18
|
flow: Workflow,
|
|
33
19
|
};
|
package/src/main.tsx
CHANGED
|
@@ -13,7 +13,6 @@ import { createIntelRouter } from "@/router/router.tsx";
|
|
|
13
13
|
import { SignInRefused } from "@/sign-in-refused/sign-in-refused.tsx";
|
|
14
14
|
import { applyTheme, readThemeChoice, SystemThemeQuery, themeFor } from "@/theme/theme.ts";
|
|
15
15
|
import { ThemeProvider } from "@/theme/theme-context.tsx";
|
|
16
|
-
import { TimezoneProvider } from "@/timezone/timezone-context.tsx";
|
|
17
16
|
import "./styles.css";
|
|
18
17
|
import "./theme/custom.css";
|
|
19
18
|
|
|
@@ -55,15 +54,13 @@ function paint(): void {
|
|
|
55
54
|
place for it. */}
|
|
56
55
|
<I18nProvider languages={languages}>
|
|
57
56
|
<ThemeProvider store={window.localStorage}>
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
<
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
)}
|
|
66
|
-
</TimezoneProvider>
|
|
57
|
+
{refused ? (
|
|
58
|
+
<SignInRefused />
|
|
59
|
+
) : (
|
|
60
|
+
<QueryClientProvider client={queryClient}>
|
|
61
|
+
<RouterProvider router={router} />
|
|
62
|
+
</QueryClientProvider>
|
|
63
|
+
)}
|
|
67
64
|
</ThemeProvider>
|
|
68
65
|
</I18nProvider>
|
|
69
66
|
</StrictMode>,
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
DocumentLinkInlineType,
|
|
5
5
|
type Node,
|
|
6
6
|
type NodeDocument,
|
|
7
|
-
} from "@anchrd/intel-contract";
|
|
7
|
+
} from "@anchrd/intel-contract/node";
|
|
8
8
|
import { filterSuggestionItems } from "@blocknote/core";
|
|
9
9
|
import { SuggestionMenuController, useCreateBlockNote } from "@blocknote/react";
|
|
10
10
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { RelationGraph } from "@anchrd/intel-contract";
|
|
1
|
+
import type { RelationGraph } from "@anchrd/intel-contract/flow";
|
|
2
2
|
import { ControlsContainer, SigmaContainer, useCamera, useRegisterEvents } from "@react-sigma/core";
|
|
3
3
|
import { LocateFixed, Minus, Plus } from "lucide-react";
|
|
4
4
|
import { useEffect, useMemo } from "react";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Node } from "@anchrd/intel-contract";
|
|
1
|
+
import type { Node } from "@anchrd/intel-contract/node";
|
|
2
2
|
import { useMutation, useQuery } from "@tanstack/react-query";
|
|
3
3
|
import { Download } from "lucide-react";
|
|
4
4
|
import { ActionSlot } from "@/app/action-slot/action-slot.tsx";
|
|
@@ -9,8 +9,8 @@ import { useIntelRouterContext } from "@/router/router-context.ts";
|
|
|
9
9
|
* A table as a grid (#40).
|
|
10
10
|
*
|
|
11
11
|
* Reading is the whole of the first cut: there is no cell editor, and rows arrive through
|
|
12
|
-
* `
|
|
13
|
-
* retyping anything.
|
|
12
|
+
* `node_table_row_create` — the point of the kind is that a flow or an MCP client fills it
|
|
13
|
+
* without a person retyping anything. Editing in the grid is anchrd/intel#165.
|
|
14
14
|
*
|
|
15
15
|
* ⚠️ The rows are the server's answer, parsed by the server's one CSV reader. A second reader here
|
|
16
16
|
* would eventually disagree with it about a quoted comma, and the grid would then show something
|
package/src/nodes/nodes.tsx
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import type { Node } from "@anchrd/intel-contract";
|
|
1
|
+
import type { Node } from "@anchrd/intel-contract/node";
|
|
2
2
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
|
3
3
|
import { useNavigate, useRouterState } from "@tanstack/react-router";
|
|
4
4
|
import { Download, History, Paperclip } from "lucide-react";
|
|
5
5
|
import { lazy, Suspense, useState } from "react";
|
|
6
|
-
import { AgentAvatar } from "@/agent/agent-avatar/agent-avatar.tsx";
|
|
7
6
|
import { ActionSlot } from "@/app/action-slot/action-slot.tsx";
|
|
8
7
|
import { ViewToggle } from "@/app/view-toggle/view-toggle.tsx";
|
|
9
8
|
import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from "@/components/ui/tooltip";
|
|
@@ -18,17 +17,6 @@ import { TitleRow } from "@/title-row/title-row.tsx";
|
|
|
18
17
|
const NodeEditor = lazy(async () => ({
|
|
19
18
|
default: (await import("@/node-editor/node-editor.tsx")).NodeEditor,
|
|
20
19
|
}));
|
|
21
|
-
// Lazy for the same reason the editor is: assistant-ui and its stream client are the agent page's
|
|
22
|
-
// alone, and every other screen would otherwise pay for them on first paint.
|
|
23
|
-
const AgentPanel = lazy(async () => ({
|
|
24
|
-
default: (await import("@/agent/agent.tsx")).AgentPanel,
|
|
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
|
-
}));
|
|
32
20
|
|
|
33
21
|
export function Nodes() {
|
|
34
22
|
const { data } = useIntelRouterContext();
|
|
@@ -121,8 +109,6 @@ export function Nodes() {
|
|
|
121
109
|
title={selected.title}
|
|
122
110
|
description={selected.description}
|
|
123
111
|
target={{ type: "node", node: selected }}
|
|
124
|
-
leading={selected.kind === "agent" ? <AgentAvatar title={selected.title} /> : null}
|
|
125
|
-
divider={selected.kind !== "agent"}
|
|
126
112
|
>
|
|
127
113
|
{selected.kind !== "folder" && (
|
|
128
114
|
<TooltipProvider delayDuration={300}>
|
|
@@ -157,26 +143,6 @@ export function Nodes() {
|
|
|
157
143
|
<AttachmentPanel node={selected} />
|
|
158
144
|
) : selected.kind === "table" ? (
|
|
159
145
|
<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>
|
|
170
|
-
) : selected.kind === "agent" ? (
|
|
171
|
-
// ⚠️ Before the editor branch, and never reaching it: an agent's version body is a
|
|
172
|
-
// definition document, not prose, and BlockNote would try to open JSON as a text.
|
|
173
|
-
<Suspense
|
|
174
|
-
fallback={
|
|
175
|
-
<p className="p-6 text-sm text-muted-foreground">{i18n.t("common.loading")}</p>
|
|
176
|
-
}
|
|
177
|
-
>
|
|
178
|
-
<AgentPanel key={selected.id} node={selected} />
|
|
179
|
-
</Suspense>
|
|
180
146
|
) : document.data ? (
|
|
181
147
|
<Suspense
|
|
182
148
|
fallback={
|