@signalsafe/tree-spec-editor 0.2.3 → 0.3.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 (65) hide show
  1. package/README.md +74 -25
  2. package/dist/index.d.ts +8 -10
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +7 -10
  5. package/dist/lib/PanelHeaderCollapseCarets.d.ts.map +1 -1
  6. package/dist/lib/PanelHeaderCollapseCarets.js +2 -1
  7. package/dist/lib/panelHelpers.d.ts +8 -1
  8. package/dist/lib/panelHelpers.d.ts.map +1 -1
  9. package/dist/lib/panelHelpers.js +14 -6
  10. package/dist/lib/panelNodeLink.d.ts.map +1 -1
  11. package/dist/lib/panelNodeLink.js +2 -1
  12. package/dist/lib/selectionStyles.d.ts +6 -6
  13. package/dist/lib/selectionStyles.d.ts.map +1 -1
  14. package/dist/lib/selectionStyles.js +6 -6
  15. package/dist/modals/AuditLogModal.d.ts +0 -6
  16. package/dist/modals/AuditLogModal.d.ts.map +1 -1
  17. package/dist/modals/AuditLogModal.js +6 -5
  18. package/dist/modals/DraftHistoryModal.d.ts +0 -10
  19. package/dist/modals/DraftHistoryModal.d.ts.map +1 -1
  20. package/dist/modals/DraftHistoryModal.js +6 -5
  21. package/dist/modals/PublishReviewModal.d.ts.map +1 -1
  22. package/dist/modals/PublishReviewModal.js +3 -2
  23. package/dist/panels/AdvancedJsonPanel.d.ts +1 -1
  24. package/dist/panels/AdvancedJsonPanel.d.ts.map +1 -1
  25. package/dist/panels/AdvancedJsonPanel.js +3 -3
  26. package/dist/panels/AppearancePanel.d.ts.map +1 -1
  27. package/dist/panels/AppearancePanel.js +6 -8
  28. package/dist/panels/ChoiceEdgeAppearanceFields.d.ts.map +1 -1
  29. package/dist/panels/ChoiceEdgeAppearanceFields.js +4 -3
  30. package/dist/panels/ChoiceTemplatesPanel.d.ts.map +1 -1
  31. package/dist/panels/ChoiceTemplatesPanel.js +3 -2
  32. package/dist/panels/GraphEditorInfoPanel.d.ts.map +1 -1
  33. package/dist/panels/GraphEditorInfoPanel.js +5 -4
  34. package/dist/panels/InspectorPanel.d.ts.map +1 -1
  35. package/dist/panels/InspectorPanel.js +1 -1
  36. package/dist/panels/IssuesPanel.d.ts.map +1 -1
  37. package/dist/panels/IssuesPanel.js +4 -5
  38. package/dist/panels/NodesPanel.d.ts.map +1 -1
  39. package/dist/panels/NodesPanel.js +7 -6
  40. package/dist/panels/SelectedEdgePanel.d.ts.map +1 -1
  41. package/dist/panels/SelectedEdgePanel.js +2 -1
  42. package/dist/panels/ToolbarPanel.d.ts +6 -18
  43. package/dist/panels/ToolbarPanel.d.ts.map +1 -1
  44. package/dist/panels/ToolbarPanel.js +10 -24
  45. package/dist/panels/inspector/ChoiceEditorCard.d.ts.map +1 -1
  46. package/dist/panels/inspector/ChoiceEditorCard.js +5 -4
  47. package/dist/panels/inspector/ChoiceEditorList.d.ts.map +1 -1
  48. package/dist/panels/inspector/ChoiceEditorList.js +3 -1
  49. package/dist/panels/inspector/NodeTypeField.d.ts.map +1 -1
  50. package/dist/panels/inspector/NodeTypeField.js +4 -3
  51. package/dist/panels/inspector/RequiredNodeCard.d.ts.map +1 -1
  52. package/dist/panels/inspector/RequiredNodeCard.js +3 -2
  53. package/dist/panels/inspector/choiceActionIconProps.d.ts +3 -2
  54. package/dist/panels/inspector/choiceActionIconProps.d.ts.map +1 -1
  55. package/dist/panels/inspector/choiceActionIconProps.js +4 -2
  56. package/dist/ui/editorClasses.d.ts +66 -0
  57. package/dist/ui/editorClasses.d.ts.map +1 -0
  58. package/dist/ui/editorClasses.js +89 -0
  59. package/dist/ui/primitives.d.ts +23 -0
  60. package/dist/ui/primitives.d.ts.map +1 -0
  61. package/dist/ui/primitives.js +32 -0
  62. package/docs/UI_KIT_AGNOSTIC_REFACTOR.md +186 -0
  63. package/docs/UI_KIT_AGNOSTIC_USAGE.md +129 -0
  64. package/package.json +8 -9
  65. package/README.standalone.md +0 -14
@@ -1,7 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
- import { Form } from 'react-bootstrap';
3
2
  import { END_NODE_ID, getTransition, } from '@signalsafe/tree-spec-editor-core';
4
3
  import { LIST_SELECTION_CLASS, LIST_SELECTION_TEXT_CLASS } from '../../lib/selectionStyles.js';
4
+ import { EDITOR_FLEX_ALIGN_END, EDITOR_FLEX_BETWEEN, EDITOR_FLEX_FILL, EDITOR_FLEX_GROW_1, EDITOR_FLEX_ROW, EDITOR_FLEX_SHRINK_0, EDITOR_LIST_ITEM, EDITOR_MIN_W_0, EDITOR_SPACING_MB_0, EDITOR_SPACING_MB_1, EDITOR_SPACING_MB_2, EDITOR_SPACING_MT_0, EDITOR_SPACING_MT_2, joinClasses, } from '../../ui/editorClasses.js';
5
+ import { EditorField, EditorInput, EditorLabel, EditorSelect, } from '../../ui/primitives.js';
5
6
  import { choiceActionIconProps } from './choiceActionIconProps.js';
6
7
  export default function ChoiceEditorCard({ choice, tree, selectedNode, focusChoiceId, isPublished, canMoveUp, canMoveDown, usedChoiceTypeIds, onUpdateSelectedNode, onDeleteChoice, onMoveChoice, onSetChoiceTarget, onSetChoiceOutcome, onSetChoiceType, choiceTypes, outcomeOptions, hideOutcomeField, renderExtraChoiceFields, onFocusChoice, }) {
7
8
  const transition = getTransition(tree, selectedNode.id, choice.id);
@@ -12,18 +13,18 @@ export default function ChoiceEditorCard({ choice, tree, selectedNode, focusChoi
12
13
  const knownTypeIds = new Set((choiceTypes ?? []).map((type) => type.id));
13
14
  const showTypeSelect = Boolean(choiceTypes?.length && onSetChoiceType);
14
15
  const isCustomType = showTypeSelect && !knownTypeIds.has(choice.id);
15
- return (_jsxs("div", { id: `choice-editor-${selectedNode.id}-${choice.id}`, className: `list-group-item py-3 px-3${isFocused ? ` ${LIST_SELECTION_CLASS} ${LIST_SELECTION_TEXT_CLASS}` : ''}`, onFocusCapture: () => onFocusChoice?.(choice.id), children: [_jsxs("div", { className: "d-flex w-100 justify-content-between align-items-end gap-2 mb-2", children: [_jsxs("div", { className: "d-flex flex-grow-1 min-w-0 gap-2", children: [showTypeSelect ? (_jsxs(Form.Group, { className: "mb-0 flex-fill min-w-0", children: [_jsx(Form.Label, { className: "mb-1", children: "Type" }), _jsxs(Form.Select, { value: isCustomType ? '__custom__' : choice.id, disabled: isPublished, "aria-label": `Choice type for ${choice.id}`, onChange: (event) => {
16
+ return (_jsxs("div", { id: `choice-editor-${selectedNode.id}-${choice.id}`, className: joinClasses(EDITOR_LIST_ITEM, isFocused && LIST_SELECTION_CLASS, isFocused && LIST_SELECTION_TEXT_CLASS), onFocusCapture: () => onFocusChoice?.(choice.id), children: [_jsxs("div", { className: joinClasses(EDITOR_FLEX_BETWEEN, EDITOR_SPACING_MB_2, EDITOR_FLEX_ALIGN_END), children: [_jsxs("div", { className: joinClasses(EDITOR_FLEX_ROW, EDITOR_FLEX_GROW_1, EDITOR_MIN_W_0, 'graph-editor-flex--gap'), children: [showTypeSelect ? (_jsxs(EditorField, { className: joinClasses(EDITOR_SPACING_MB_0, EDITOR_FLEX_FILL, EDITOR_MIN_W_0), children: [_jsx(EditorLabel, { className: EDITOR_SPACING_MB_1, children: "Type" }), _jsxs(EditorSelect, { value: isCustomType ? '__custom__' : choice.id, disabled: isPublished, "aria-label": `Choice type for ${choice.id}`, onChange: (event) => {
16
17
  const nextTypeId = event.target.value;
17
18
  if (nextTypeId && nextTypeId !== '__custom__') {
18
19
  const type = choiceTypes.find((item) => item.id === nextTypeId);
19
20
  onSetChoiceType(choice.id, nextTypeId, type?.label);
20
21
  }
21
- }, children: [choiceTypes.map((type) => (_jsx("option", { value: type.id, disabled: type.id !== choice.id && usedChoiceTypeIds.has(type.id), title: type.label, children: type.id }, type.id))), isCustomType ? (_jsxs("option", { value: "__custom__", children: [choice.id, " (custom)"] })) : null] })] })) : (_jsxs(Form.Group, { className: "mb-0 flex-fill min-w-0", children: [_jsx(Form.Label, { className: "mb-1", children: "Type" }), _jsx(Form.Control, { plaintext: true, readOnly: true, className: "text-uppercase fw-semibold", value: choice.id })] })), hideOutcomeField ? null : (_jsxs(Form.Group, { className: "mb-0 flex-fill min-w-0", children: [_jsx(Form.Label, { className: "mb-1", children: "Outcome" }), showOutcome ? (_jsx(Form.Select, { value: outcomeValue, disabled: isPublished, "aria-label": `Outcome for ${choice.id}`, onChange: (e) => onSetChoiceOutcome(choice.id, e.target.value), children: outcomeOptions.map((opt) => (_jsx("option", { value: opt.value, children: opt.label }, opt.value))) })) : (_jsx(Form.Select, { disabled: true, "aria-label": `Outcome for ${choice.id}`, value: "", children: _jsx("option", { value: "", children: "When Next is END" }) }))] }))] }), _jsxs("div", { className: "d-flex align-items-center gap-2 flex-shrink-0", children: [onMoveChoice ? (_jsxs(_Fragment, { children: [_jsx("i", { ...choiceActionIconProps(() => onMoveChoice(choice.id, 'up'), 'Move choice up', 'bi bi-chevron-up text-secondary', isPublished || !canMoveUp) }), _jsx("i", { ...choiceActionIconProps(() => onMoveChoice(choice.id, 'down'), 'Move choice down', 'bi bi-chevron-down text-secondary', isPublished || !canMoveDown) })] })) : null, _jsx("i", { ...choiceActionIconProps(() => onDeleteChoice(choice.id), 'Delete choice', 'bi bi-trash text-danger') })] })] }), _jsx(Form.Label, { className: "mt-0", children: "Label" }), _jsx(Form.Control, { value: choice.label, onChange: (event) => {
22
+ }, children: [choiceTypes.map((type) => (_jsx("option", { value: type.id, disabled: type.id !== choice.id && usedChoiceTypeIds.has(type.id), title: type.label, children: type.id }, type.id))), isCustomType ? (_jsxs("option", { value: "__custom__", children: [choice.id, " (custom)"] })) : null] })] })) : (_jsxs(EditorField, { className: joinClasses(EDITOR_SPACING_MB_0, EDITOR_FLEX_FILL, EDITOR_MIN_W_0), children: [_jsx(EditorLabel, { className: EDITOR_SPACING_MB_1, children: "Type" }), _jsx(EditorInput, { readOnly: true, className: "graph-editor-input--plain graph-editor-text--uppercase graph-editor-text--semibold", value: choice.id })] })), hideOutcomeField ? null : (_jsxs(EditorField, { className: joinClasses(EDITOR_SPACING_MB_0, EDITOR_FLEX_FILL, EDITOR_MIN_W_0), children: [_jsx(EditorLabel, { className: EDITOR_SPACING_MB_1, children: "Outcome" }), showOutcome ? (_jsx(EditorSelect, { value: outcomeValue, disabled: isPublished, "aria-label": `Outcome for ${choice.id}`, onChange: (e) => onSetChoiceOutcome(choice.id, e.target.value), children: outcomeOptions.map((opt) => (_jsx("option", { value: opt.value, children: opt.label }, opt.value))) })) : (_jsx(EditorSelect, { disabled: true, "aria-label": `Outcome for ${choice.id}`, value: "", children: _jsx("option", { value: "", children: "When Next is END" }) }))] }))] }), _jsxs("div", { className: joinClasses(EDITOR_FLEX_ROW, EDITOR_FLEX_SHRINK_0, 'graph-editor-flex--gap'), children: [onMoveChoice ? (_jsxs(_Fragment, { children: [_jsx("span", { ...choiceActionIconProps(() => onMoveChoice(choice.id, 'up'), 'Move choice up', undefined, isPublished || !canMoveUp, '↑') }), _jsx("span", { ...choiceActionIconProps(() => onMoveChoice(choice.id, 'down'), 'Move choice down', undefined, isPublished || !canMoveDown, '↓') })] })) : null, _jsx("span", { ...choiceActionIconProps(() => onDeleteChoice(choice.id), 'Delete choice', 'graph-editor-action-icon--danger', isPublished, '×') })] })] }), _jsx(EditorLabel, { className: EDITOR_SPACING_MT_0, children: "Label" }), _jsx(EditorInput, { value: choice.label, onChange: (event) => {
22
23
  const updatedChoices = (selectedNode.choices ?? []).map((existingChoice) => existingChoice.id === choice.id
23
24
  ? { ...existingChoice, label: event.target.value }
24
25
  : existingChoice);
25
26
  onUpdateSelectedNode({ choices: updatedChoices });
26
- } }), _jsx(Form.Label, { className: "mt-2", children: "Next" }), _jsxs(Form.Select, { value: targetNodeId, onChange: (e) => onSetChoiceTarget(choice.id, e.target.value), children: [_jsx("option", { value: "", children: "(not connected)" }), Object.values(tree.nodes).map((n) => (_jsxs("option", { value: n.id, children: [n.id, " \u00B7 ", n.prompt?.slice(0, 40) || n.type] }, n.id))), _jsx("option", { value: END_NODE_ID, children: "END" })] }), renderExtraChoiceFields
27
+ } }), _jsx(EditorLabel, { className: EDITOR_SPACING_MT_2, children: "Next" }), _jsxs(EditorSelect, { value: targetNodeId, onChange: (e) => onSetChoiceTarget(choice.id, e.target.value), children: [_jsx("option", { value: "", children: "(not connected)" }), Object.values(tree.nodes).map((n) => (_jsxs("option", { value: n.id, children: [n.id, " \u00B7 ", n.prompt?.slice(0, 40) || n.type] }, n.id))), _jsx("option", { value: END_NODE_ID, children: "END" })] }), renderExtraChoiceFields
27
28
  ? renderExtraChoiceFields({
28
29
  tree,
29
30
  node: selectedNode,
@@ -1 +1 @@
1
- {"version":3,"file":"ChoiceEditorList.d.ts","sourceRoot":"","sources":["../../../src/panels/inspector/ChoiceEditorList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAIhF,OAAO,KAAK,EAAE,gBAAgB,EAAE,4BAA4B,EAAE,MAAM,SAAS,CAAC;AAE9E,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACrC,IAAI,EACJ,YAAY,EACZ,aAAa,EACb,WAAW,EACX,WAAW,EACX,oBAAoB,EACpB,cAAc,EACd,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,uBAAuB,EACvB,aAAa,GAChB,EAAE,QAAQ,CAAC;IACR,IAAI,EAAE,UAAU,CAAC;IACjB,YAAY,EAAE,UAAU,CAAC;IACzB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,oBAAoB,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;IAC3D,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,GAAG,MAAM,KAAK,IAAI,CAAC;IACpE,iBAAiB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACpE,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAChE,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACpF,WAAW,CAAC,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC9C,cAAc,EAAE,aAAa,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChE,gBAAgB,EAAE,OAAO,CAAC;IAC1B,uBAAuB,CAAC,EAAE,CAAC,GAAG,EAAE,4BAA4B,KAAK,SAAS,CAAC;IAC3E,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C,CAAC,+BAqED"}
1
+ {"version":3,"file":"ChoiceEditorList.d.ts","sourceRoot":"","sources":["../../../src/panels/inspector/ChoiceEditorList.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAsBhF,OAAO,KAAK,EAAE,gBAAgB,EAAE,4BAA4B,EAAE,MAAM,YAAY,CAAC;AAEjF,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACrC,IAAI,EACJ,YAAY,EACZ,aAAa,EACb,WAAW,EACX,WAAW,EACX,oBAAoB,EACpB,cAAc,EACd,YAAY,EACZ,iBAAiB,EACjB,kBAAkB,EAClB,eAAe,EACf,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,uBAAuB,EACvB,aAAa,GAChB,EAAE,QAAQ,CAAC;IACR,IAAI,EAAE,UAAU,CAAC;IACjB,YAAY,EAAE,UAAU,CAAC;IACzB,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,WAAW,EAAE,OAAO,CAAC;IACrB,WAAW,EAAE,MAAM,IAAI,CAAC;IACxB,oBAAoB,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;IAC3D,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;IAC3C,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,IAAI,GAAG,MAAM,KAAK,IAAI,CAAC;IACpE,iBAAiB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;IACpE,kBAAkB,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IAChE,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACpF,WAAW,CAAC,EAAE,aAAa,CAAC,gBAAgB,CAAC,CAAC;IAC9C,cAAc,EAAE,aAAa,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChE,gBAAgB,EAAE,OAAO,CAAC;IAC1B,uBAAuB,CAAC,EAAE,CAAC,GAAG,EAAE,4BAA4B,KAAK,SAAS,CAAC;IAC3E,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CAC9C,CAAC,+BAqED"}
@@ -1,10 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState } from 'react';
3
3
  import PanelHeaderCollapseCarets from '../../lib/PanelHeaderCollapseCarets.js';
4
+ import { EDITOR_CARD, EDITOR_CARD_BODY, EDITOR_CARD_HEADER, EDITOR_FLEX_BETWEEN, EDITOR_FLEX_ROW, EDITOR_FLEX_SHRINK_0, EDITOR_HIDDEN, EDITOR_LIST, EDITOR_MIN_W_0, EDITOR_MUTED, EDITOR_SCROLL, EDITOR_SPACING_MT_3, EDITOR_SPACING_PX_3, EDITOR_SPACING_PY_3, joinClasses, } from '../../ui/editorClasses.js';
5
+ import { EditorIconButton } from '../../ui/primitives.js';
4
6
  import ChoiceEditorCard from './ChoiceEditorCard.js';
5
7
  export default function ChoiceEditorList({ tree, selectedNode, focusChoiceId, isPublished, onAddChoice, onUpdateSelectedNode, onDeleteChoice, onMoveChoice, onSetChoiceTarget, onSetChoiceOutcome, onSetChoiceType, choiceTypes, outcomeOptions, hideOutcomeField, renderExtraChoiceFields, onFocusChoice, }) {
6
8
  const choices = selectedNode.choices ?? [];
7
9
  const [choicesExpanded, setChoicesExpanded] = useState(true);
8
10
  const usedChoiceTypeIds = new Set(choices.map((choice) => choice.id));
9
- return (_jsxs("div", { className: "card mt-3", children: [_jsxs("div", { className: "card-header bg-body-secondary py-2 px-2 d-flex justify-content-between align-items-center gap-2", children: [_jsxs("div", { className: "d-flex align-items-center min-w-0", children: [_jsx("span", { className: "fw-semibold", children: "Choices" }), _jsx(PanelHeaderCollapseCarets, { expanded: choicesExpanded, onToggle: () => setChoicesExpanded((v) => !v) })] }), _jsx("div", { className: "d-flex align-items-center gap-1 flex-shrink-0", children: _jsx("button", { type: "button", className: "btn p-0 border-0 bg-transparent flex-shrink-0", "aria-label": "Add choice", title: "Add choice", disabled: isPublished, onClick: onAddChoice, children: _jsx("i", { className: `bi bi-plus-lg action-icon${isPublished ? ' text-secondary opacity-50' : ' text-primary cursor-pointer'}`, "aria-hidden": true }) }) })] }), _jsx("div", { className: `card-body p-0${choicesExpanded ? '' : ' d-none'}`, "aria-hidden": !choicesExpanded, children: choices.length === 0 ? (_jsx("div", { className: "text-muted small py-3 px-3", children: _jsx("em", { children: "No choices" }) })) : (_jsx("div", { className: "list-group list-group-flush overflow-auto-max-h-320", children: choices.map((choice, index) => (_jsx(ChoiceEditorCard, { choice: choice, tree: tree, selectedNode: selectedNode, focusChoiceId: focusChoiceId, isPublished: isPublished, canMoveUp: index > 0, canMoveDown: index < choices.length - 1, usedChoiceTypeIds: usedChoiceTypeIds, onUpdateSelectedNode: onUpdateSelectedNode, onDeleteChoice: onDeleteChoice, onMoveChoice: onMoveChoice, onSetChoiceTarget: onSetChoiceTarget, onSetChoiceOutcome: onSetChoiceOutcome, onSetChoiceType: onSetChoiceType, choiceTypes: choiceTypes, outcomeOptions: outcomeOptions, hideOutcomeField: hideOutcomeField, renderExtraChoiceFields: renderExtraChoiceFields, onFocusChoice: onFocusChoice }, choice.id))) })) })] }));
11
+ return (_jsxs("div", { className: joinClasses(EDITOR_CARD, EDITOR_SPACING_MT_3), children: [_jsxs("div", { className: joinClasses(EDITOR_CARD_HEADER, EDITOR_FLEX_BETWEEN), children: [_jsxs("div", { className: joinClasses(EDITOR_FLEX_ROW, EDITOR_MIN_W_0), children: [_jsx("span", { className: "graph-editor-text--semibold", children: "Choices" }), _jsx(PanelHeaderCollapseCarets, { expanded: choicesExpanded, onToggle: () => setChoicesExpanded((v) => !v) })] }), _jsx("div", { className: joinClasses(EDITOR_FLEX_ROW, EDITOR_FLEX_SHRINK_0), children: _jsx(EditorIconButton, { className: joinClasses(EDITOR_FLEX_SHRINK_0, isPublished ? 'graph-editor-btn--disabled' : 'graph-editor-btn--primary'), "aria-label": "Add choice", title: "Add choice", disabled: isPublished, onClick: onAddChoice, children: "+" }) })] }), _jsx("div", { className: joinClasses(EDITOR_CARD_BODY, 'graph-editor-card__body--flush', !choicesExpanded && EDITOR_HIDDEN), "aria-hidden": !choicesExpanded, children: choices.length === 0 ? (_jsx("div", { className: joinClasses(EDITOR_MUTED, 'graph-editor-text--sm', EDITOR_SPACING_PY_3, EDITOR_SPACING_PX_3), children: _jsx("em", { children: "No choices" }) })) : (_jsx("div", { className: joinClasses(EDITOR_LIST, EDITOR_SCROLL, 'overflow-auto-max-h-320'), children: choices.map((choice, index) => (_jsx(ChoiceEditorCard, { choice: choice, tree: tree, selectedNode: selectedNode, focusChoiceId: focusChoiceId, isPublished: isPublished, canMoveUp: index > 0, canMoveDown: index < choices.length - 1, usedChoiceTypeIds: usedChoiceTypeIds, onUpdateSelectedNode: onUpdateSelectedNode, onDeleteChoice: onDeleteChoice, onMoveChoice: onMoveChoice, onSetChoiceTarget: onSetChoiceTarget, onSetChoiceOutcome: onSetChoiceOutcome, onSetChoiceType: onSetChoiceType, choiceTypes: choiceTypes, outcomeOptions: outcomeOptions, hideOutcomeField: hideOutcomeField, renderExtraChoiceFields: renderExtraChoiceFields, onFocusChoice: onFocusChoice }, choice.id))) })) })] }));
10
12
  }
@@ -1 +1 @@
1
- {"version":3,"file":"NodeTypeField.d.ts","sourceRoot":"","sources":["../../../src/panels/inspector/NodeTypeField.tsx"],"names":[],"mappings":"AAGA,OAAO,EAEH,KAAK,UAAU,EAClB,MAAM,mCAAmC,CAAC;AAE3C,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,EAClC,YAAY,EACZ,WAAW,EACX,oBAAoB,EACpB,cAAc,GACjB,EAAE,QAAQ,CAAC;IACR,YAAY,EAAE,UAAU,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,oBAAoB,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;IAC3D,cAAc,EAAE,MAAM,CAAC;CAC1B,CAAC,+BAkCD"}
1
+ {"version":3,"file":"NodeTypeField.d.ts","sourceRoot":"","sources":["../../../src/panels/inspector/NodeTypeField.tsx"],"names":[],"mappings":"AAEA,OAAO,EAEH,KAAK,UAAU,EAClB,MAAM,mCAAmC,CAAC;AAW3C,MAAM,CAAC,OAAO,UAAU,aAAa,CAAC,EAClC,YAAY,EACZ,WAAW,EACX,oBAAoB,EACpB,cAAc,GACjB,EAAE,QAAQ,CAAC;IACR,YAAY,EAAE,UAAU,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,oBAAoB,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;IAC3D,cAAc,EAAE,MAAM,CAAC;CAC1B,CAAC,+BAkCD"}
@@ -1,12 +1,13 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { Form } from 'react-bootstrap';
3
2
  import { TREE_SPEC_NODE_TYPE_PRESETS, } from '@signalsafe/tree-spec-editor-core';
3
+ import { EDITOR_MUTED, EDITOR_SPACING_MB_2, EDITOR_SPACING_MT_1, EDITOR_SPACING_MT_2, joinClasses, } from '../../ui/editorClasses.js';
4
+ import { EditorField, EditorInput, EditorLabel, EditorSelect } from '../../ui/primitives.js';
4
5
  export default function NodeTypeField({ selectedNode, isPublished, onUpdateSelectedNode, typeHelperText, }) {
5
6
  const isPreset = TREE_SPEC_NODE_TYPE_PRESETS.includes(selectedNode.type);
6
- return (_jsxs(Form.Group, { className: "mb-2", children: [_jsx(Form.Label, { children: "Type" }), _jsxs(Form.Select, { value: isPreset ? selectedNode.type : '__custom__', onChange: (e) => {
7
+ return (_jsxs(EditorField, { className: EDITOR_SPACING_MB_2, children: [_jsx(EditorLabel, { children: "Type" }), _jsxs(EditorSelect, { value: isPreset ? selectedNode.type : '__custom__', onChange: (e) => {
7
8
  const nextType = e.target.value;
8
9
  if (nextType === '__custom__')
9
10
  return;
10
11
  onUpdateSelectedNode({ type: nextType });
11
- }, disabled: isPublished, children: [TREE_SPEC_NODE_TYPE_PRESETS.map((t) => (_jsx("option", { value: t, children: t }, t))), _jsx("option", { value: "__custom__", children: "custom\u2026" })] }), isPreset ? null : (_jsx(Form.Control, { className: "mt-2", value: selectedNode.type, onChange: (e) => onUpdateSelectedNode({ type: e.target.value }), placeholder: "Enter custom type\u2026", disabled: isPublished })), _jsx("div", { className: "text-muted mt-1 font-size-12", children: typeHelperText })] }));
12
+ }, disabled: isPublished, children: [TREE_SPEC_NODE_TYPE_PRESETS.map((t) => (_jsx("option", { value: t, children: t }, t))), _jsx("option", { value: "__custom__", children: "custom\u2026" })] }), isPreset ? null : (_jsx(EditorInput, { className: EDITOR_SPACING_MT_2, value: selectedNode.type, onChange: (e) => onUpdateSelectedNode({ type: e.target.value }), placeholder: "Enter custom type\u2026", disabled: isPublished })), _jsx("div", { className: joinClasses(EDITOR_MUTED, 'graph-editor-text--sm', EDITOR_SPACING_MT_1), children: typeHelperText })] }));
12
13
  }
@@ -1 +1 @@
1
- {"version":3,"file":"RequiredNodeCard.d.ts","sourceRoot":"","sources":["../../../src/panels/inspector/RequiredNodeCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,SAAS,EAAE,MAAM,OAAO,CAAC;AAIpD,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAIhF,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,SAAS,CAAC;AAE1D,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACrC,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,KAAK,EACL,cAAc,EACd,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,GACxB,EAAE,QAAQ,CAAC;IACR,IAAI,EAAE,UAAU,CAAC;IACjB,YAAY,EAAE,UAAU,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,oBAAoB,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;IAC3D,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;IAClC,qBAAqB,CAAC,EAAE,CAAC,GAAG,EAAE,0BAA0B,KAAK,SAAS,CAAC;CAC1E,CAAC,+BA4DD"}
1
+ {"version":3,"file":"RequiredNodeCard.d.ts","sourceRoot":"","sources":["../../../src/panels/inspector/RequiredNodeCard.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAe,SAAS,EAAE,MAAM,OAAO,CAAC;AAGpD,OAAO,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,mCAAmC,CAAC;AAiBhF,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAE7D,MAAM,CAAC,OAAO,UAAU,gBAAgB,CAAC,EACrC,IAAI,EACJ,YAAY,EACZ,WAAW,EACX,KAAK,EACL,cAAc,EACd,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,GACxB,EAAE,QAAQ,CAAC;IACR,IAAI,EAAE,UAAU,CAAC;IACjB,YAAY,EAAE,UAAU,CAAC;IACzB,WAAW,EAAE,OAAO,CAAC;IACrB,KAAK,EAAE,MAAM,CAAC;IACd,cAAc,EAAE,MAAM,CAAC;IACvB,oBAAoB,EAAE,CAAC,KAAK,EAAE,OAAO,CAAC,UAAU,CAAC,KAAK,IAAI,CAAC;IAC3D,oBAAoB,CAAC,EAAE,MAAM,IAAI,CAAC;IAClC,qBAAqB,CAAC,EAAE,CAAC,GAAG,EAAE,0BAA0B,KAAK,SAAS,CAAC;CAC1E,CAAC,+BA2DD"}
@@ -1,11 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useState } from 'react';
3
- import { Form } from 'react-bootstrap';
4
3
  import PanelHeaderCollapseCarets from '../../lib/PanelHeaderCollapseCarets.js';
4
+ import { EDITOR_CARD, EDITOR_CARD_BODY, EDITOR_CARD_HEADER, EDITOR_FLEX_BETWEEN, EDITOR_FLEX_ROW, EDITOR_FLEX_SHRINK_0, EDITOR_HIDDEN, EDITOR_MIN_W_0, EDITOR_SPACING_MB_2, joinClasses, } from '../../ui/editorClasses.js';
5
+ import { EditorField, EditorIconButton, EditorLabel, EditorTextarea } from '../../ui/primitives.js';
5
6
  import NodeTypeField from './NodeTypeField.js';
6
7
  export default function RequiredNodeCard({ tree, selectedNode, isPublished, title, typeHelperText, onUpdateSelectedNode, onDeleteSelectedNode, renderExtraNodeFields, }) {
7
8
  const [requiredExpanded, setRequiredExpanded] = useState(true);
8
- return (_jsxs("div", { className: "card", children: [_jsxs("div", { className: "card-header bg-body-secondary py-2 px-2 d-flex justify-content-between align-items-center gap-2", children: [_jsxs("div", { className: "d-flex align-items-center min-w-0", children: [_jsx("span", { className: "fw-semibold", children: title }), _jsx(PanelHeaderCollapseCarets, { expanded: requiredExpanded, onToggle: () => setRequiredExpanded((v) => !v) })] }), onDeleteSelectedNode ? (_jsx("button", { type: "button", className: "btn p-0 border-0 bg-transparent flex-shrink-0", "aria-label": "Delete node", title: "Delete node", disabled: isPublished, onClick: onDeleteSelectedNode, children: _jsx("i", { className: `bi bi-trash action-icon cursor-pointer${isPublished ? ' text-secondary opacity-50' : ' text-danger'}`, "aria-hidden": true }) })) : null] }), _jsxs("div", { className: `card-body${requiredExpanded ? '' : ' d-none'}`, "aria-hidden": !requiredExpanded, children: [_jsx(NodeTypeField, { selectedNode: selectedNode, isPublished: isPublished, onUpdateSelectedNode: onUpdateSelectedNode, typeHelperText: typeHelperText }), _jsxs(Form.Group, { className: "mb-2", children: [_jsx(Form.Label, { children: "Prompt" }), _jsx(Form.Control, { as: "textarea", rows: 4, value: selectedNode.prompt, onChange: (e) => onUpdateSelectedNode({ prompt: e.target.value }) })] }), renderExtraNodeFields
9
+ return (_jsxs("div", { className: EDITOR_CARD, children: [_jsxs("div", { className: joinClasses(EDITOR_CARD_HEADER, EDITOR_FLEX_BETWEEN), children: [_jsxs("div", { className: joinClasses(EDITOR_FLEX_ROW, EDITOR_MIN_W_0), children: [_jsx("span", { className: "graph-editor-text--semibold", children: title }), _jsx(PanelHeaderCollapseCarets, { expanded: requiredExpanded, onToggle: () => setRequiredExpanded((v) => !v) })] }), onDeleteSelectedNode ? (_jsx(EditorIconButton, { className: joinClasses(EDITOR_FLEX_SHRINK_0, isPublished ? 'graph-editor-btn--disabled' : 'graph-editor-btn--danger'), "aria-label": "Delete node", title: "Delete node", disabled: isPublished, onClick: onDeleteSelectedNode, children: "\uD83D\uDDD1" })) : null] }), _jsxs("div", { className: joinClasses(EDITOR_CARD_BODY, !requiredExpanded && EDITOR_HIDDEN), "aria-hidden": !requiredExpanded, children: [_jsx(NodeTypeField, { selectedNode: selectedNode, isPublished: isPublished, onUpdateSelectedNode: onUpdateSelectedNode, typeHelperText: typeHelperText }), _jsxs(EditorField, { className: EDITOR_SPACING_MB_2, children: [_jsx(EditorLabel, { children: "Prompt" }), _jsx(EditorTextarea, { rows: 4, value: selectedNode.prompt, onChange: (e) => onUpdateSelectedNode({ prompt: e.target.value }) })] }), renderExtraNodeFields
9
10
  ? renderExtraNodeFields({
10
11
  tree,
11
12
  node: selectedNode,
@@ -1,11 +1,12 @@
1
- import type { KeyboardEvent, MouseEvent } from 'react';
2
- export declare function choiceActionIconProps(onClick: () => void, title: string, className: string, disabled?: boolean): {
1
+ import type { KeyboardEvent, MouseEvent, ReactNode } from 'react';
2
+ export declare function choiceActionIconProps(onClick: () => void, title: string, className?: string, disabled?: boolean, children?: ReactNode): {
3
3
  role: "button";
4
4
  tabIndex: number;
5
5
  title: string;
6
6
  'aria-label': string;
7
7
  'aria-disabled': boolean | undefined;
8
8
  className: string;
9
+ children: ReactNode;
9
10
  onClick: ((event: MouseEvent<HTMLElement>) => void) | undefined;
10
11
  onKeyDown: ((event: KeyboardEvent<HTMLElement>) => void) | undefined;
11
12
  };
@@ -1 +1 @@
1
- {"version":3,"file":"choiceActionIconProps.d.ts","sourceRoot":"","sources":["../../../src/panels/inspector/choiceActionIconProps.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AAEvD,wBAAgB,qBAAqB,CACjC,OAAO,EAAE,MAAM,IAAI,EACnB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,QAAQ,UAAQ;;;;;;;sBAaE,UAAU,CAAC,WAAW,CAAC;wBAOvB,aAAa,CAAC,WAAW,CAAC;EAQ/C"}
1
+ {"version":3,"file":"choiceActionIconProps.d.ts","sourceRoot":"","sources":["../../../src/panels/inspector/choiceActionIconProps.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAIlE,wBAAgB,qBAAqB,CACjC,OAAO,EAAE,MAAM,IAAI,EACnB,KAAK,EAAE,MAAM,EACb,SAAS,CAAC,EAAE,MAAM,EAClB,QAAQ,UAAQ,EAChB,QAAQ,CAAC,EAAE,SAAS;;;;;;;;sBAgBF,UAAU,CAAC,WAAW,CAAC;wBAOvB,aAAa,CAAC,WAAW,CAAC;EAQ/C"}
@@ -1,11 +1,13 @@
1
- export function choiceActionIconProps(onClick, title, className, disabled = false) {
1
+ import { joinClasses } from '../../ui/editorClasses.js';
2
+ export function choiceActionIconProps(onClick, title, className, disabled = false, children) {
2
3
  return {
3
4
  role: 'button',
4
5
  tabIndex: disabled ? -1 : 0,
5
6
  title,
6
7
  'aria-label': title,
7
8
  'aria-disabled': disabled ? true : undefined,
8
- className: `${className} action-icon flex-shrink-0${disabled ? ' text-secondary opacity-50' : ' cursor-pointer'}`,
9
+ className: joinClasses('graph-editor-action-icon', className, disabled && 'graph-editor-action-icon--disabled'),
10
+ children,
9
11
  onClick: disabled
10
12
  ? undefined
11
13
  : (event) => {
@@ -0,0 +1,66 @@
1
+ /** UI-kit agnostic layout and shell class hooks (host styles via CSS or UI library). */
2
+ export declare const EDITOR_CARD = "graph-editor-card";
3
+ export declare const EDITOR_CARD_HEADER = "graph-editor-card__header";
4
+ export declare const EDITOR_CARD_BODY = "graph-editor-card__body";
5
+ export declare const EDITOR_CARD_FOOTER = "graph-editor-card__footer";
6
+ export declare const EDITOR_LIST = "graph-editor-list";
7
+ export declare const EDITOR_LIST_ITEM = "graph-editor-list__item";
8
+ export declare const EDITOR_INPUT = "graph-editor-input";
9
+ export declare const EDITOR_SELECT = "graph-editor-select";
10
+ export declare const EDITOR_TEXTAREA = "graph-editor-textarea";
11
+ export declare const EDITOR_CHECKBOX = "graph-editor-checkbox";
12
+ export declare const EDITOR_BTN = "graph-editor-btn";
13
+ export declare const EDITOR_BTN_ICON = "graph-editor-btn graph-editor-btn--icon";
14
+ export declare const EDITOR_BADGE = "graph-editor-badge";
15
+ export declare const EDITOR_MODAL = "graph-editor-modal";
16
+ export declare const EDITOR_MODAL_DIALOG = "graph-editor-modal__dialog";
17
+ export declare const EDITOR_MODAL_CONTENT = "graph-editor-modal__content";
18
+ export declare const EDITOR_MODAL_HEADER = "graph-editor-modal__header";
19
+ export declare const EDITOR_MODAL_BODY = "graph-editor-modal__body";
20
+ export declare const EDITOR_MODAL_FOOTER = "graph-editor-modal__footer";
21
+ export declare const EDITOR_MODAL_TITLE = "graph-editor-modal__title";
22
+ export declare const EDITOR_ALERT = "graph-editor-alert";
23
+ export declare const EDITOR_TABLE = "graph-editor-table";
24
+ export declare const EDITOR_DROPDOWN = "graph-editor-dropdown";
25
+ export declare const EDITOR_DROPDOWN_MENU = "graph-editor-dropdown__menu";
26
+ export declare const EDITOR_DROPDOWN_ITEM = "graph-editor-dropdown__item";
27
+ export declare const EDITOR_DROPDOWN_DIVIDER = "graph-editor-dropdown__divider";
28
+ export declare const EDITOR_FIELD = "graph-editor-field";
29
+ export declare const EDITOR_FIELD_LABEL = "graph-editor-field__label";
30
+ export declare const EDITOR_FLEX = "graph-editor-flex";
31
+ export declare const EDITOR_FLEX_BETWEEN = "graph-editor-flex graph-editor-flex--between";
32
+ export declare const EDITOR_FLEX_ROW = "graph-editor-flex graph-editor-flex--row";
33
+ export declare const EDITOR_FLEX_ALIGN_END = "graph-editor-flex graph-editor-flex--align-end";
34
+ export declare const EDITOR_FLEX_WRAP = "graph-editor-flex graph-editor-flex--wrap";
35
+ export declare const EDITOR_ALIGN_TOP = "graph-editor-align-top";
36
+ export declare const EDITOR_MIN_W_0 = "graph-editor-min-w-0";
37
+ export declare const EDITOR_FLEX_SHRINK_0 = "graph-editor-flex-shrink-0";
38
+ export declare const EDITOR_FLEX_GROW_1 = "graph-editor-flex-grow-1";
39
+ export declare const EDITOR_FLEX_FILL = "graph-editor-flex-fill";
40
+ export declare const EDITOR_TEXT_BREAK = "graph-editor-text--break";
41
+ export declare const EDITOR_TEXT_START = "graph-editor-text--start";
42
+ export declare const EDITOR_TEXT_END = "graph-editor-text--end";
43
+ export declare const EDITOR_SPACING_MB_0 = "graph-editor-spacing--mb-0";
44
+ export declare const EDITOR_SPACING_MB_1 = "graph-editor-spacing--mb-1";
45
+ export declare const EDITOR_SPACING_MB_2 = "graph-editor-spacing--mb-2";
46
+ export declare const EDITOR_SPACING_MB_3 = "graph-editor-spacing--mb-3";
47
+ export declare const EDITOR_SPACING_MT_0 = "graph-editor-spacing--mt-0";
48
+ export declare const EDITOR_SPACING_MT_1 = "graph-editor-spacing--mt-1";
49
+ export declare const EDITOR_SPACING_MT_2 = "graph-editor-spacing--mt-2";
50
+ export declare const EDITOR_SPACING_MT_3 = "graph-editor-spacing--mt-3";
51
+ export declare const EDITOR_SPACING_ME_2 = "graph-editor-spacing--me-2";
52
+ export declare const EDITOR_SPACING_MS_AUTO = "graph-editor-spacing--ms-auto";
53
+ export declare const EDITOR_SPACING_PY_2 = "graph-editor-spacing--py-2";
54
+ export declare const EDITOR_SPACING_PY_3 = "graph-editor-spacing--py-3";
55
+ export declare const EDITOR_SPACING_PX_3 = "graph-editor-spacing--px-3";
56
+ export declare const EDITOR_SPACING_PT_2 = "graph-editor-spacing--pt-2";
57
+ export declare const EDITOR_SPACING_M_0 = "graph-editor-spacing--m-0";
58
+ export declare const EDITOR_MUTED = "graph-editor-muted";
59
+ export declare const EDITOR_MONO = "graph-editor-mono";
60
+ export declare const EDITOR_HIDDEN = "graph-editor-hidden";
61
+ export declare const EDITOR_SCROLL = "graph-editor-scroll";
62
+ export declare function editorBtnToneClass(variant?: string): string;
63
+ export declare function editorBadgeToneClass(variant?: string): string;
64
+ export declare function editorAlertToneClass(tone: 'info' | 'danger'): string;
65
+ export declare function joinClasses(...parts: Array<string | false | null | undefined>): string;
66
+ //# sourceMappingURL=editorClasses.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editorClasses.d.ts","sourceRoot":"","sources":["../../src/ui/editorClasses.ts"],"names":[],"mappings":"AAAA,wFAAwF;AAExF,eAAO,MAAM,WAAW,sBAAsB,CAAC;AAC/C,eAAO,MAAM,kBAAkB,8BAA8B,CAAC;AAC9D,eAAO,MAAM,gBAAgB,4BAA4B,CAAC;AAC1D,eAAO,MAAM,kBAAkB,8BAA8B,CAAC;AAE9D,eAAO,MAAM,WAAW,sBAAsB,CAAC;AAC/C,eAAO,MAAM,gBAAgB,4BAA4B,CAAC;AAE1D,eAAO,MAAM,YAAY,uBAAuB,CAAC;AACjD,eAAO,MAAM,aAAa,wBAAwB,CAAC;AACnD,eAAO,MAAM,eAAe,0BAA0B,CAAC;AACvD,eAAO,MAAM,eAAe,0BAA0B,CAAC;AAEvD,eAAO,MAAM,UAAU,qBAAqB,CAAC;AAC7C,eAAO,MAAM,eAAe,4CAA4C,CAAC;AACzE,eAAO,MAAM,YAAY,uBAAuB,CAAC;AAEjD,eAAO,MAAM,YAAY,uBAAuB,CAAC;AACjD,eAAO,MAAM,mBAAmB,+BAA+B,CAAC;AAChE,eAAO,MAAM,oBAAoB,gCAAgC,CAAC;AAClE,eAAO,MAAM,mBAAmB,+BAA+B,CAAC;AAChE,eAAO,MAAM,iBAAiB,6BAA6B,CAAC;AAC5D,eAAO,MAAM,mBAAmB,+BAA+B,CAAC;AAChE,eAAO,MAAM,kBAAkB,8BAA8B,CAAC;AAE9D,eAAO,MAAM,YAAY,uBAAuB,CAAC;AACjD,eAAO,MAAM,YAAY,uBAAuB,CAAC;AAEjD,eAAO,MAAM,eAAe,0BAA0B,CAAC;AACvD,eAAO,MAAM,oBAAoB,gCAAgC,CAAC;AAClE,eAAO,MAAM,oBAAoB,gCAAgC,CAAC;AAClE,eAAO,MAAM,uBAAuB,mCAAmC,CAAC;AAExE,eAAO,MAAM,YAAY,uBAAuB,CAAC;AACjD,eAAO,MAAM,kBAAkB,8BAA8B,CAAC;AAE9D,eAAO,MAAM,WAAW,sBAAsB,CAAC;AAC/C,eAAO,MAAM,mBAAmB,iDAAiD,CAAC;AAClF,eAAO,MAAM,eAAe,6CAA6C,CAAC;AAC1E,eAAO,MAAM,qBAAqB,mDAAmD,CAAC;AACtF,eAAO,MAAM,gBAAgB,8CAA8C,CAAC;AAC5E,eAAO,MAAM,gBAAgB,2BAA2B,CAAC;AACzD,eAAO,MAAM,cAAc,yBAAyB,CAAC;AACrD,eAAO,MAAM,oBAAoB,+BAA+B,CAAC;AACjE,eAAO,MAAM,kBAAkB,6BAA6B,CAAC;AAC7D,eAAO,MAAM,gBAAgB,2BAA2B,CAAC;AACzD,eAAO,MAAM,iBAAiB,6BAA6B,CAAC;AAC5D,eAAO,MAAM,iBAAiB,6BAA6B,CAAC;AAC5D,eAAO,MAAM,eAAe,2BAA2B,CAAC;AACxD,eAAO,MAAM,mBAAmB,+BAA+B,CAAC;AAChE,eAAO,MAAM,mBAAmB,+BAA+B,CAAC;AAChE,eAAO,MAAM,mBAAmB,+BAA+B,CAAC;AAChE,eAAO,MAAM,mBAAmB,+BAA+B,CAAC;AAChE,eAAO,MAAM,mBAAmB,+BAA+B,CAAC;AAChE,eAAO,MAAM,mBAAmB,+BAA+B,CAAC;AAChE,eAAO,MAAM,mBAAmB,+BAA+B,CAAC;AAChE,eAAO,MAAM,mBAAmB,+BAA+B,CAAC;AAChE,eAAO,MAAM,mBAAmB,+BAA+B,CAAC;AAChE,eAAO,MAAM,sBAAsB,kCAAkC,CAAC;AACtE,eAAO,MAAM,mBAAmB,+BAA+B,CAAC;AAChE,eAAO,MAAM,mBAAmB,+BAA+B,CAAC;AAChE,eAAO,MAAM,mBAAmB,+BAA+B,CAAC;AAChE,eAAO,MAAM,mBAAmB,+BAA+B,CAAC;AAChE,eAAO,MAAM,kBAAkB,8BAA8B,CAAC;AAC9D,eAAO,MAAM,YAAY,uBAAuB,CAAC;AACjD,eAAO,MAAM,WAAW,sBAAsB,CAAC;AAC/C,eAAO,MAAM,aAAa,wBAAwB,CAAC;AACnD,eAAO,MAAM,aAAa,wBAAwB,CAAC;AAEnD,wBAAgB,kBAAkB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAG3D;AAED,wBAAgB,oBAAoB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,CAG7D;AAED,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,MAAM,GAAG,QAAQ,GAAG,MAAM,CAEpE;AAgBD,wBAAgB,WAAW,CAAC,GAAG,KAAK,EAAE,KAAK,CAAC,MAAM,GAAG,KAAK,GAAG,IAAI,GAAG,SAAS,CAAC,GAAG,MAAM,CAEtF"}
@@ -0,0 +1,89 @@
1
+ /** UI-kit agnostic layout and shell class hooks (host styles via CSS or UI library). */
2
+ export const EDITOR_CARD = 'graph-editor-card';
3
+ export const EDITOR_CARD_HEADER = 'graph-editor-card__header';
4
+ export const EDITOR_CARD_BODY = 'graph-editor-card__body';
5
+ export const EDITOR_CARD_FOOTER = 'graph-editor-card__footer';
6
+ export const EDITOR_LIST = 'graph-editor-list';
7
+ export const EDITOR_LIST_ITEM = 'graph-editor-list__item';
8
+ export const EDITOR_INPUT = 'graph-editor-input';
9
+ export const EDITOR_SELECT = 'graph-editor-select';
10
+ export const EDITOR_TEXTAREA = 'graph-editor-textarea';
11
+ export const EDITOR_CHECKBOX = 'graph-editor-checkbox';
12
+ export const EDITOR_BTN = 'graph-editor-btn';
13
+ export const EDITOR_BTN_ICON = 'graph-editor-btn graph-editor-btn--icon';
14
+ export const EDITOR_BADGE = 'graph-editor-badge';
15
+ export const EDITOR_MODAL = 'graph-editor-modal';
16
+ export const EDITOR_MODAL_DIALOG = 'graph-editor-modal__dialog';
17
+ export const EDITOR_MODAL_CONTENT = 'graph-editor-modal__content';
18
+ export const EDITOR_MODAL_HEADER = 'graph-editor-modal__header';
19
+ export const EDITOR_MODAL_BODY = 'graph-editor-modal__body';
20
+ export const EDITOR_MODAL_FOOTER = 'graph-editor-modal__footer';
21
+ export const EDITOR_MODAL_TITLE = 'graph-editor-modal__title';
22
+ export const EDITOR_ALERT = 'graph-editor-alert';
23
+ export const EDITOR_TABLE = 'graph-editor-table';
24
+ export const EDITOR_DROPDOWN = 'graph-editor-dropdown';
25
+ export const EDITOR_DROPDOWN_MENU = 'graph-editor-dropdown__menu';
26
+ export const EDITOR_DROPDOWN_ITEM = 'graph-editor-dropdown__item';
27
+ export const EDITOR_DROPDOWN_DIVIDER = 'graph-editor-dropdown__divider';
28
+ export const EDITOR_FIELD = 'graph-editor-field';
29
+ export const EDITOR_FIELD_LABEL = 'graph-editor-field__label';
30
+ export const EDITOR_FLEX = 'graph-editor-flex';
31
+ export const EDITOR_FLEX_BETWEEN = 'graph-editor-flex graph-editor-flex--between';
32
+ export const EDITOR_FLEX_ROW = 'graph-editor-flex graph-editor-flex--row';
33
+ export const EDITOR_FLEX_ALIGN_END = 'graph-editor-flex graph-editor-flex--align-end';
34
+ export const EDITOR_FLEX_WRAP = 'graph-editor-flex graph-editor-flex--wrap';
35
+ export const EDITOR_ALIGN_TOP = 'graph-editor-align-top';
36
+ export const EDITOR_MIN_W_0 = 'graph-editor-min-w-0';
37
+ export const EDITOR_FLEX_SHRINK_0 = 'graph-editor-flex-shrink-0';
38
+ export const EDITOR_FLEX_GROW_1 = 'graph-editor-flex-grow-1';
39
+ export const EDITOR_FLEX_FILL = 'graph-editor-flex-fill';
40
+ export const EDITOR_TEXT_BREAK = 'graph-editor-text--break';
41
+ export const EDITOR_TEXT_START = 'graph-editor-text--start';
42
+ export const EDITOR_TEXT_END = 'graph-editor-text--end';
43
+ export const EDITOR_SPACING_MB_0 = 'graph-editor-spacing--mb-0';
44
+ export const EDITOR_SPACING_MB_1 = 'graph-editor-spacing--mb-1';
45
+ export const EDITOR_SPACING_MB_2 = 'graph-editor-spacing--mb-2';
46
+ export const EDITOR_SPACING_MB_3 = 'graph-editor-spacing--mb-3';
47
+ export const EDITOR_SPACING_MT_0 = 'graph-editor-spacing--mt-0';
48
+ export const EDITOR_SPACING_MT_1 = 'graph-editor-spacing--mt-1';
49
+ export const EDITOR_SPACING_MT_2 = 'graph-editor-spacing--mt-2';
50
+ export const EDITOR_SPACING_MT_3 = 'graph-editor-spacing--mt-3';
51
+ export const EDITOR_SPACING_ME_2 = 'graph-editor-spacing--me-2';
52
+ export const EDITOR_SPACING_MS_AUTO = 'graph-editor-spacing--ms-auto';
53
+ export const EDITOR_SPACING_PY_2 = 'graph-editor-spacing--py-2';
54
+ export const EDITOR_SPACING_PY_3 = 'graph-editor-spacing--py-3';
55
+ export const EDITOR_SPACING_PX_3 = 'graph-editor-spacing--px-3';
56
+ export const EDITOR_SPACING_PT_2 = 'graph-editor-spacing--pt-2';
57
+ export const EDITOR_SPACING_M_0 = 'graph-editor-spacing--m-0';
58
+ export const EDITOR_MUTED = 'graph-editor-muted';
59
+ export const EDITOR_MONO = 'graph-editor-mono';
60
+ export const EDITOR_HIDDEN = 'graph-editor-hidden';
61
+ export const EDITOR_SCROLL = 'graph-editor-scroll';
62
+ export function editorBtnToneClass(variant) {
63
+ const tone = normalizeToolbarTone(variant);
64
+ return `${EDITOR_BTN} graph-editor-btn--${tone}`;
65
+ }
66
+ export function editorBadgeToneClass(variant) {
67
+ const tone = normalizeToolbarTone(variant ?? 'neutral');
68
+ return `${EDITOR_BADGE} graph-editor-badge--${tone}`;
69
+ }
70
+ export function editorAlertToneClass(tone) {
71
+ return `${EDITOR_ALERT} graph-editor-alert--${tone}`;
72
+ }
73
+ function normalizeToolbarTone(variant) {
74
+ if (!variant)
75
+ return 'neutral';
76
+ const map = {
77
+ 'outline-secondary': 'neutral',
78
+ secondary: 'neutral',
79
+ success: 'success',
80
+ primary: 'primary',
81
+ danger: 'danger',
82
+ info: 'info',
83
+ warning: 'warning',
84
+ };
85
+ return map[variant] ?? variant.replace(/^outline-/, '');
86
+ }
87
+ export function joinClasses(...parts) {
88
+ return parts.filter(Boolean).join(' ');
89
+ }
@@ -0,0 +1,23 @@
1
+ import type { ButtonHTMLAttributes, InputHTMLAttributes, LabelHTMLAttributes, ReactNode, SelectHTMLAttributes, TextareaHTMLAttributes } from 'react';
2
+ export declare function EditorButton({ tone, className, children, ...rest }: ButtonHTMLAttributes<HTMLButtonElement> & {
3
+ tone?: string;
4
+ }): import("react").JSX.Element;
5
+ export declare function EditorIconButton({ className, children, ...rest }: ButtonHTMLAttributes<HTMLButtonElement>): import("react").JSX.Element;
6
+ export declare function EditorCloseButton({ className, label, ...rest }: ButtonHTMLAttributes<HTMLButtonElement> & {
7
+ label?: string;
8
+ }): import("react").JSX.Element;
9
+ export declare function EditorInput({ className, ...rest }: InputHTMLAttributes<HTMLInputElement>): import("react").JSX.Element;
10
+ export declare function EditorSelect({ className, children, ...rest }: SelectHTMLAttributes<HTMLSelectElement>): import("react").JSX.Element;
11
+ export declare function EditorTextarea({ className, ...rest }: TextareaHTMLAttributes<HTMLTextAreaElement>): import("react").JSX.Element;
12
+ export declare function EditorField({ className, children, }: {
13
+ className?: string;
14
+ children: ReactNode;
15
+ }): import("react").JSX.Element;
16
+ export declare function EditorLabel({ className, children, ...rest }: LabelHTMLAttributes<HTMLLabelElement>): import("react").JSX.Element;
17
+ export declare function EditorCheckbox({ className, label, ...rest }: InputHTMLAttributes<HTMLInputElement> & {
18
+ label?: ReactNode;
19
+ }): import("react").JSX.Element;
20
+ export declare function EditorSwitch({ className, label, ...rest }: InputHTMLAttributes<HTMLInputElement> & {
21
+ label?: ReactNode;
22
+ }): import("react").JSX.Element;
23
+ //# sourceMappingURL=primitives.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"primitives.d.ts","sourceRoot":"","sources":["../../src/ui/primitives.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,oBAAoB,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,SAAS,EAAE,oBAAoB,EAAE,sBAAsB,EAAE,MAAM,OAAO,CAAC;AAcrJ,wBAAgB,YAAY,CAAC,EACzB,IAAI,EACJ,SAAS,EACT,QAAQ,EACR,GAAG,IAAI,EACV,EAAE,oBAAoB,CAAC,iBAAiB,CAAC,GAAG;IAAE,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,+BAM7D;AAED,wBAAgB,gBAAgB,CAAC,EAC7B,SAAS,EACT,QAAQ,EACR,GAAG,IAAI,EACV,EAAE,oBAAoB,CAAC,iBAAiB,CAAC,+BAMzC;AAED,wBAAgB,iBAAiB,CAAC,EAC9B,SAAS,EACT,KAAe,EACf,GAAG,IAAI,EACV,EAAE,oBAAoB,CAAC,iBAAiB,CAAC,GAAG;IAAE,KAAK,CAAC,EAAE,MAAM,CAAA;CAAE,+BAW9D;AAED,wBAAgB,WAAW,CAAC,EACxB,SAAS,EACT,GAAG,IAAI,EACV,EAAE,mBAAmB,CAAC,gBAAgB,CAAC,+BAEvC;AAED,wBAAgB,YAAY,CAAC,EACzB,SAAS,EACT,QAAQ,EACR,GAAG,IAAI,EACV,EAAE,oBAAoB,CAAC,iBAAiB,CAAC,+BAMzC;AAED,wBAAgB,cAAc,CAAC,EAC3B,SAAS,EACT,GAAG,IAAI,EACV,EAAE,sBAAsB,CAAC,mBAAmB,CAAC,+BAE7C;AAED,wBAAgB,WAAW,CAAC,EACxB,SAAS,EACT,QAAQ,GACX,EAAE;IACC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,SAAS,CAAC;CACvB,+BAEA;AAED,wBAAgB,WAAW,CAAC,EACxB,SAAS,EACT,QAAQ,EACR,GAAG,IAAI,EACV,EAAE,mBAAmB,CAAC,gBAAgB,CAAC,+BAMvC;AAED,wBAAgB,cAAc,CAAC,EAC3B,SAAS,EACT,KAAK,EACL,GAAG,IAAI,EACV,EAAE,mBAAmB,CAAC,gBAAgB,CAAC,GAAG;IAAE,KAAK,CAAC,EAAE,SAAS,CAAA;CAAE,+BAO/D;AAED,wBAAgB,YAAY,CAAC,EACzB,SAAS,EACT,KAAK,EACL,GAAG,IAAI,EACV,EAAE,mBAAmB,CAAC,gBAAgB,CAAC,GAAG;IAAE,KAAK,CAAC,EAAE,SAAS,CAAA;CAAE,+BAO/D"}
@@ -0,0 +1,32 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { EDITOR_BTN, EDITOR_CHECKBOX, EDITOR_FIELD, EDITOR_FIELD_LABEL, EDITOR_INPUT, EDITOR_SELECT, EDITOR_TEXTAREA, editorBtnToneClass, joinClasses, } from './editorClasses.js';
3
+ export function EditorButton({ tone, className, children, ...rest }) {
4
+ return (_jsx("button", { type: "button", className: joinClasses(editorBtnToneClass(tone), className), ...rest, children: children }));
5
+ }
6
+ export function EditorIconButton({ className, children, ...rest }) {
7
+ return (_jsx("button", { type: "button", className: joinClasses(`${EDITOR_BTN} graph-editor-btn--icon`, className), ...rest, children: children }));
8
+ }
9
+ export function EditorCloseButton({ className, label = 'Close', ...rest }) {
10
+ return (_jsx("button", { type: "button", className: joinClasses(`${EDITOR_BTN} graph-editor-btn--close`, className), "aria-label": label, ...rest, children: "\u00D7" }));
11
+ }
12
+ export function EditorInput({ className, ...rest }) {
13
+ return _jsx("input", { className: joinClasses(EDITOR_INPUT, className), ...rest });
14
+ }
15
+ export function EditorSelect({ className, children, ...rest }) {
16
+ return (_jsx("select", { className: joinClasses(EDITOR_SELECT, className), ...rest, children: children }));
17
+ }
18
+ export function EditorTextarea({ className, ...rest }) {
19
+ return _jsx("textarea", { className: joinClasses(EDITOR_TEXTAREA, className), ...rest });
20
+ }
21
+ export function EditorField({ className, children, }) {
22
+ return _jsx("div", { className: joinClasses(EDITOR_FIELD, className), children: children });
23
+ }
24
+ export function EditorLabel({ className, children, ...rest }) {
25
+ return (_jsx("label", { className: joinClasses(EDITOR_FIELD_LABEL, className), ...rest, children: children }));
26
+ }
27
+ export function EditorCheckbox({ className, label, ...rest }) {
28
+ return (_jsxs("label", { className: joinClasses(EDITOR_CHECKBOX, className), children: [_jsx("input", { type: "checkbox", ...rest }), label ? _jsx("span", { children: label }) : null] }));
29
+ }
30
+ export function EditorSwitch({ className, label, ...rest }) {
31
+ return (_jsxs("label", { className: joinClasses(`${EDITOR_CHECKBOX} graph-editor-checkbox--switch`, className), children: [_jsx("input", { type: "checkbox", role: "switch", ...rest }), label ? _jsx("span", { children: label }) : null] }));
32
+ }
@@ -0,0 +1,186 @@
1
+ # UI-kit agnostic editor refactor — design scan (Prompt 8)
2
+
3
+ **Status:** Implemented on `cleanup` (Prompts 9–10, 9A). `@signalsafe/tree-spec-editor@0.3.0` and `@signalsafe/tree-spec-editor-react@0.2.0` are UI-kit agnostic. See [UI_KIT_AGNOSTIC_USAGE.md](./UI_KIT_AGNOSTIC_USAGE.md) and [`tree-spec-editor-react` canvas doc](../../tree-spec-editor-react/docs/UI_KIT_AGNOSTIC_CANVAS.md).
4
+
5
+ ---
6
+
7
+ ## Executive summary (historical — Prompt 8 scan)
8
+
9
+ | Package | react-bootstrap | Bootstrap CSS classes | Ready for UI-kit agnostic goal |
10
+ |---------|-----------------|----------------------|--------------------------------|
11
+ | `tree-spec-editor-core` | None | None | **Yes** — already meets boundary |
12
+ | `tree-spec-editor-react` | None | **None (0.2.0+)** | **Yes** — canvas uses `graph-editor-*` hooks |
13
+ | `tree-spec-editor` | ~~**Yes** — 13 source files~~ → **None (0.3.0+)** | ~~**Yes**~~ → **`graph-editor-*` hooks** | **Yes** — shell refactor complete |
14
+
15
+ **Likely semver:** `@signalsafe/tree-spec-editor` **0.3.0** (remove `react-bootstrap` peer; public panel/modal API and styling contract change). `@signalsafe/tree-spec-editor-react` **0.2.0** (canvas class renames / neutral tokens). `@signalsafe/tree-spec-editor-core` **patch or unchanged**.
16
+
17
+ ---
18
+
19
+ ## Search inventory (Prompt 8 task list)
20
+
21
+ ### `tree-spec-editor`
22
+
23
+ | Category | Matches |
24
+ |----------|---------|
25
+ | **Runtime `react-bootstrap` imports** | 13 files under `src/` (see table below) |
26
+ | **Bootstrap CSS only (no import)** | Modals (`<dialog>` + `modal-*`, `alert-*`), all sidebar panels (`card-*`, `list-group-*`, `btn-*`), `panelNodeLink.tsx`, `PanelHeaderCollapseCarets.tsx` |
27
+ | **Public exports (Bootstrap-coupled)** | All panel/modal components; `ToolbarPanel`; `getIssueSeverityBadgeClass`; `LIST_*` / `CANVAS_*` selection class constants; `buildDefaultToolbarSpec` (variant strings) |
28
+ | **Package metadata** | `peerDependencies.react-bootstrap`, keyword `react-bootstrap`, README install line includes `react-bootstrap bootstrap` |
29
+ | **Tests** | `ToolbarPanel.test.ts` mocks `react-bootstrap`; `panelHelpers.test.ts` asserts `text-bg-*`; `NodesPanel` / `IssuesPanel` assert `list-group-item-primary` |
30
+ | **Docs** | README positions package as “React **Bootstrap UI shell**” (throughout) |
31
+ | **Not used** | `Tabs`, `Accordion`, `Overlay`, `Popover`, `Toast`, `react-bootstrap` `Modal` (native `<dialog>` + Bootstrap classes instead) |
32
+
33
+ **Runtime `react-bootstrap` import sites:**
34
+
35
+ | File | Components imported |
36
+ |------|---------------------|
37
+ | `src/modals/DraftHistoryModal.tsx` | `Button`, `CloseButton` |
38
+ | `src/modals/AuditLogModal.tsx` | `Button`, `CloseButton` |
39
+ | `src/modals/PublishReviewModal.tsx` | `Button`, `CloseButton` |
40
+ | `src/panels/GraphEditorInfoPanel.tsx` | `Badge`, `Form`, `Table` |
41
+ | `src/panels/AdvancedJsonPanel.tsx` | `Button` |
42
+ | `src/panels/IssuesPanel.tsx` | `Form` |
43
+ | `src/panels/NodesPanel.tsx` | `Form` |
44
+ | `src/panels/ChoiceEdgeAppearanceFields.tsx` | `Form` |
45
+ | `src/panels/inspector/ChoiceEditorCard.tsx` | `Form` |
46
+ | `src/panels/inspector/NodeTypeField.tsx` | `Form` |
47
+ | `src/panels/inspector/RequiredNodeCard.tsx` | `Form` |
48
+ | `src/panels/ToolbarPanel.tsx` | `Button`, `Dropdown` |
49
+ | `src/panels/ChoiceTemplatesPanel.tsx` | `Button`, `Card` |
50
+
51
+ **Public API — types mentioning Bootstrap:**
52
+
53
+ - `ToolbarButtonItem.variant`, `ToolbarDropdownItem.variant`, `ToolbarBadgeItem.variant` — documented as Bootstrap variant strings.
54
+ - `PublishReviewModalProps.errorAlertText` — uses Bootstrap `alert-danger` markup (prop name only; rendering is Bootstrap CSS).
55
+
56
+ ### `tree-spec-editor-react`
57
+
58
+ | Category | Matches |
59
+ |----------|---------|
60
+ | **react-bootstrap** | **None** (docs/comments only) |
61
+ | **Bootstrap CSS in runtime** | 9 files: `PromptNode.tsx`, `PromptNodeHeader.tsx`, `PromptNodeChoicesList.tsx`, `PromptNodeIssueBadges.tsx`, `PromptNodeToolbar.tsx`, `ChoiceCanvasRow.tsx`, `EndNode.tsx`, `GraphCanvasContextMenu.tsx`, `canvas/constants.ts` (`dropdown-menu`, `card-*`, `badge`, `btn-*`, `list-group-*`, `bg-body-secondary`) |
62
+ | **False positives** | `useTreeSpecEditor` “bootstrap graph” comments (data bootstrap, not UI) |
63
+ | **Public API** | `TreeSpecGraphEditorProps.colorScheme` doc mentions Bootstrap `colorScheme` — host contract leak |
64
+
65
+ ### `tree-spec-editor-core`
66
+
67
+ | Category | Matches |
68
+ |----------|---------|
69
+ | **All matches** | README / `index.ts` comments stating “no Bootstrap” — **docs only** |
70
+ | **Runtime** | **Clean** |
71
+
72
+ ---
73
+
74
+ ## Current vs target boundaries
75
+
76
+ ### Target (ecosystem policy)
77
+
78
+ 1. **`tree-spec-editor-core`** — pure logic, no React/DOM/Bootstrap. **Already there.**
79
+ 2. **`tree-spec-editor-react`** — React Flow + hooks; **no Bootstrap/MUI**; neutral canvas markup.
80
+ 3. **`tree-spec-editor`** — UI-kit agnostic composition (hooks, primitives, render props); **must not** require `react-bootstrap` or Bootstrap CSS.
81
+
82
+ ### Optional future package
83
+
84
+ `@signalsafe/tree-spec-editor-bootstrap` — migrate **today’s** panel/modal/toolbar implementations here for hosts that want the reference Bootstrap shell (including DeliveryPlus during transition).
85
+
86
+ ---
87
+
88
+ ## Refactor strategy (recommended phases)
89
+
90
+ ### Phase 0 — No behavior change (prep)
91
+
92
+ - Add this design doc; align DeliveryPlus on timeline.
93
+ - Snapshot README/API contract before refactors.
94
+
95
+ ### Phase 1 — `tree-spec-editor-react` neutral canvas
96
+
97
+ - Replace Bootstrap class names in node/context-menu components with **`graph-editor-*`** semantic classes.
98
+ - Ship minimal package CSS (or document host-provided tokens) mapping semantic classes → host theme.
99
+ - Change `CONTEXT_MENU_CLASS` off `dropdown-menu` to neutral menu classes.
100
+ - Update tests in `-react` that assert Bootstrap class strings.
101
+ - **semver:** minor (`0.2.0`).
102
+
103
+ ### Phase 2 — Headless shell API in `tree-spec-editor`
104
+
105
+ Introduce UI-kit agnostic layer **alongside** existing components (deprecate, then remove):
106
+
107
+ | Pattern | Use for |
108
+ |---------|---------|
109
+ | **Hooks** | Re-export / wrap `useTreeSpecEditor` with panel-state helpers (`useInspectorState`, `useToolbarActions`) |
110
+ | **Primitives** | Unstyled structural components: `EditorSidebarSection`, `EditorModalFrame`, `EditorListRow` — semantics + ARIA only |
111
+ | **Render props / slots** | `InspectorPanel` already has `renderExtraNodeFields` / `renderExtraChoiceFields` — extend to **all** chrome (buttons, badges, inputs) |
112
+ | **Data-only helpers** | Replace `getIssueSeverityBadgeClass` with `getIssueSeverityToken()` → `'error' \| 'warning' \| 'info'`; hosts map to UI |
113
+ | **Selection tokens** | Replace exported `LIST_SELECTION_CLASS` Bootstrap strings with stable token constants |
114
+
115
+ Remove from **`tree-spec-editor` public API** (breaking):
116
+
117
+ - Direct export of Bootstrap-backed panels/modals **or** move them to `@signalsafe/tree-spec-editor-bootstrap`.
118
+
119
+ ### Phase 3 — Optional `@signalsafe/tree-spec-editor-bootstrap`
120
+
121
+ - Move current `src/panels/`, `src/modals/`, Bootstrap `ToolbarPanel` implementation verbatim.
122
+ - Keep peer deps: `react-bootstrap`, `bootstrap` (CSS loaded by host).
123
+ - `tree-spec-editor` re-exports bootstrap package temporarily with deprecation notice.
124
+
125
+ ### Phase 4 — Docs & metadata
126
+
127
+ - Remove `react-bootstrap` from `peerDependencies` and keywords on `@signalsafe/tree-spec-editor`.
128
+ - README: primary path = hooks + render props; Bootstrap = optional add-on package.
129
+ - **semver:** `@signalsafe/tree-spec-editor` **0.3.0**.
130
+
131
+ ---
132
+
133
+ ## DeliveryPlus migration impact
134
+
135
+ **Today (inferred from package docs):**
136
+
137
+ - Installs `@signalsafe/tree-spec-editor` + peers including **`react-bootstrap`** and **Bootstrap CSS**.
138
+ - Uses exported panels, modals, toolbar, and canvas default export from one barrel.
139
+
140
+ **After refactor:**
141
+
142
+ - **Short term:** Pin `@signalsafe/tree-spec-editor-bootstrap` (or pre-0.3.0 line) until DeliveryPlus rebuilds shell with its UI library.
143
+ - **Target:** Depend on `@signalsafe/tree-spec-editor-react` + `@signalsafe/tree-spec-editor-core` + **DeliveryPlus-owned** panel/modal components using render props / hooks.
144
+ - **CSS:** Must load neutral `graph-editor-*` styles from `-react` (or map tokens in DeliveryPlus theme) — no implicit Bootstrap on canvas.
145
+
146
+ **Do not modify DeliveryPlus in this prompt.**
147
+
148
+ ---
149
+
150
+ ## Risks
151
+
152
+ | Risk | Mitigation |
153
+ |------|------------|
154
+ | Breaking visual parity | Optional bootstrap package; screenshot tests in bootstrap package only |
155
+ | Class-based tests fail | Update tests to assert semantic tokens / `graph-editor-*` classes |
156
+ | Hidden Bootstrap in `-react` | Phase 1 before removing bootstrap from shell |
157
+ | Toolbar `variant` strings | Replace with theme-agnostic `tone: 'primary' \| 'neutral' \| 'danger'` |
158
+ | Modal `<dialog>` + Bootstrap layout | `EditorModalFrame` primitive with slot for actions |
159
+
160
+ ---
161
+
162
+ ## Tests needed (implementation prompts)
163
+
164
+ - [ ] `-react`: canvas renders without Bootstrap CSS loaded (layout smoke).
165
+ - [ ] `-react`: context menu keyboard/a11y with neutral classes.
166
+ - [ ] `tree-spec-editor`: hook + render-prop contract tests (no `react-bootstrap` import in package).
167
+ - [ ] `tree-spec-editor-bootstrap`: port existing panel/modal vitest suite.
168
+ - [ ] Cross-package: `buildDefaultToolbarSpec` works with agnostic toolbar renderer.
169
+ - [ ] DeliveryPlus integration checklist (manual).
170
+
171
+ ---
172
+
173
+ ## Recommended action summary
174
+
175
+ | Repo | Action |
176
+ |------|--------|
177
+ | `tree-spec-editor-core` | **None** — maintain boundary |
178
+ | `tree-spec-editor-react` | **Phase 1 ✓** — Bootstrap CSS removed from canvas (`0.2.0`) |
179
+ | `tree-spec-editor` | **Phase 2–4** — headless API + optional bootstrap split; **0.3.0** |
180
+
181
+ ---
182
+
183
+ ## References
184
+
185
+ - Ecosystem boundaries: `prompts/summary.md` (Option A — UI-kit agnostic editor)
186
+ - npm packages (release-ready on `cleanup`, not yet published): `@signalsafe/tree-spec-editor@0.3.0`, `@signalsafe/tree-spec-editor-react@0.2.0`, `@signalsafe/tree-spec-editor-core@0.1.3`