@unofficialbox/box-open-elements 0.20.0 → 0.21.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 (57) hide show
  1. package/dist/components/actions/menu-item.d.ts +2 -0
  2. package/dist/components/actions/menu-item.js +4 -0
  3. package/dist/components/collections/card.js +6 -0
  4. package/dist/components/collections/result-blocks.js +4 -1
  5. package/dist/components/feedback/alert.d.ts +2 -0
  6. package/dist/components/feedback/alert.js +17 -13
  7. package/dist/components/feedback/toast.d.ts +10 -1
  8. package/dist/components/feedback/toast.js +69 -27
  9. package/dist/components/forms/number-input.d.ts +1 -0
  10. package/dist/components/forms/number-input.js +9 -6
  11. package/dist/components/forms/search-field.js +16 -0
  12. package/dist/components/overlays/context-menu.d.ts +2 -0
  13. package/dist/components/overlays/context-menu.js +7 -0
  14. package/dist/components/overlays/dialog.js +19 -3
  15. package/dist/components/overlays/popover.js +6 -1
  16. package/dist/element-maps.d.ts +11 -1
  17. package/dist/foundations/a11y/announcer.d.ts +12 -0
  18. package/dist/foundations/a11y/announcer.js +75 -0
  19. package/dist/foundations/a11y/focus.js +46 -25
  20. package/dist/foundations/a11y/index.d.ts +2 -0
  21. package/dist/foundations/a11y/index.js +1 -0
  22. package/dist/foundations/icons/box-iconography.d.ts +472 -472
  23. package/dist/foundations/icons/box-iconography.generated.d.ts +1151 -679
  24. package/dist/foundations/icons/box-iconography.generated.js +1006 -3588
  25. package/dist/foundations/icons/glyphs/index.d.ts +1 -0
  26. package/dist/foundations/icons/glyphs/index.js +2 -0
  27. package/dist/foundations/icons/glyphs/package.json +1 -0
  28. package/dist/foundations/overlay/positioning.js +7 -0
  29. package/dist/foundations/status/index.d.ts +1 -1
  30. package/dist/foundations/status/index.js +8 -6
  31. package/dist/foundations/tokens/box-dark.js +4 -1
  32. package/dist/foundations/tokens/box-defaults.js +3 -0
  33. package/dist/foundations/tokens/registry.d.ts +3 -0
  34. package/dist/foundations/tokens/registry.js +3 -0
  35. package/dist/foundations/tokens/types.d.ts +3 -0
  36. package/dist/index.d.ts +2 -0
  37. package/dist/index.js +2 -0
  38. package/dist/patterns/agent-chat/agent-chat.js +12 -4
  39. package/dist/patterns/call-console/call-console.d.ts +3 -0
  40. package/dist/patterns/call-console/call-console.js +114 -20
  41. package/dist/patterns/content-explorer/adapters/action-menu.js +7 -5
  42. package/dist/patterns/content-explorer/adapters/toolbar.js +6 -2
  43. package/dist/patterns/file-request/file-request-builder.js +3 -0
  44. package/dist/patterns/flow-builder/flow-builder.d.ts +48 -0
  45. package/dist/patterns/flow-builder/flow-builder.js +200 -0
  46. package/dist/patterns/flow-builder/index.d.ts +3 -0
  47. package/dist/patterns/flow-builder/index.js +3 -0
  48. package/dist/patterns/flow-builder/model.d.ts +33 -0
  49. package/dist/patterns/flow-builder/model.js +28 -0
  50. package/dist/patterns/flow-builder/primitives.d.ts +47 -0
  51. package/dist/patterns/flow-builder/primitives.js +192 -0
  52. package/dist/patterns/lineage/lineage-graph.js +26 -6
  53. package/dist/patterns/notifications/notification-inbox.js +19 -3
  54. package/dist/patterns/preview/annotation-inspector.js +2 -2
  55. package/dist/patterns/undo/index.d.ts +19 -0
  56. package/dist/patterns/undo/index.js +106 -0
  57. package/package.json +11 -2
@@ -0,0 +1 @@
1
+ export { icon2Chat, icon2ChatQuestion, icon2OverlaySquares, icon3OverlaySquares, icon3SquareCircle, icon3Tier, icon4Squares, icon4Squares2, icon6Dots, iconAddSmall, iconAddSquares, iconAddToCart, iconAid, iconAppCloud, iconArrowDown, iconArrowRight, iconAttachment, iconAudio, iconAward, iconBadge, iconBag, iconBank, iconBattery, iconBell, iconBinders, iconBolt, iconBoltBubble, iconBrain, iconBriefcase, iconBroadcast, iconBrowser, iconBrush, iconCalendar, iconCalendar2, iconCalendarAdded, iconCart, iconCart2, iconCash, iconCautionFile, iconCautionPhone, iconCautionTriangle, iconCenter, iconChatBubble, iconChatQuestion, iconCheck, iconCheckmark, iconClipboard, iconClock1, iconClock2, iconClock3, iconCloud, iconCloudServer, iconCode, iconCodeBrowser, iconCompass, iconComputer, iconComputerBadge, iconComputerDownload, iconComputerUpoad, iconConnectedDots, iconConnectedTogether, iconContainerLines, iconContainerLines2, iconCubeDotLine, iconCubeLine, iconCycle, iconDesktopLaptop, iconDevices, iconDistribute, iconDistribute2, iconDna, iconDollarSign, iconDonutCoffee, iconDownload, iconDrop, iconEnterpriseBuilding, iconEnvelope, iconEnvelopeBug, iconEnvelopeDoc, iconEnvelopeShred, iconExclamationMark, iconEye, iconFileApproved, iconFileBlank, iconFileBug, iconFileCheck, iconFileCloud, iconFileCopy, iconFileDocument, iconFileImage, iconFileInfographic, iconFileInsights, iconFileMove, iconFileMusic, iconFilePlus, iconFilePresentations, iconFileSpeed, iconFileSpreadsheet, iconFileText, iconFileVideo, iconFingerPrint, iconFlight, iconFolder, iconFolderUp, iconFolderUpload, iconFtpFolder, iconFunnel, iconGear, iconGears, iconGraduationCap, iconGraphIncrease, iconGraphMeasure, iconGroupCall, iconHeart, iconHome, iconImage, iconKeySafe, iconLeavesGrowing, iconLight, iconLineGraphV2, iconLineGraph, iconLink, iconLock, iconMagnifyingGlass, iconMartini, iconMaze, iconMedTool, iconMegaphone, iconMetadata, iconMicrophone, iconMinus, iconMixedMediaDownload, iconMusicNote, iconNotePad, iconNumber0, iconNumber1, iconNumber2, iconNumber3, iconNumber4, iconNumber5, iconNumber6, iconNumber7, iconNumber8, iconNumber9, iconOMaru, iconOverlay, iconPaper, iconPen, iconPerson, iconPhone, iconPhone2, iconPhoneBrowser, iconPieChart, iconPieChart3, iconPin, iconPin2, iconPlane, iconPlay, iconPlus, iconPodium, iconPoint, iconPointCenter, iconQuestionMark, iconRaft, iconRetailTag, iconRightToForget, iconRing, iconRocket, iconScale, iconScale2, iconScan, iconServer, iconShakeLove, iconSharedUsers, iconShield2Tone, iconShieldCheck, iconShieldVarified, iconSlice, iconSlider, iconSmbBuilding, iconSolidArrowDown, iconSolidArrowRight, iconSolidCheck, iconSolidCheckmark, iconSolidClock1, iconSolidClock2, iconSolidCompass, iconSolidDollarSign, iconSolidExclamationMark, iconSolidMinus, iconSolidOMaru, iconSolidPlay, iconSolidPlus, iconSolidQuestionMark, iconSolidXBatsu, iconSpeachBubble, iconSpotlight, iconSprayBottle, iconSquaresScale, iconSquaresScaleTop, iconSquaresTogether, iconStar, iconStopHand, iconStorage, iconStorageCloud, iconStorageKey, iconSun, iconTarget, iconTeam, iconTeamAdd, iconThumb, iconToggle, iconTools, iconTrophy, iconTruck, iconTwoFiles, iconTyping, iconTypingNow, iconUpDown, iconUpload, iconUploadCloud, iconUsb, iconUserAlert, iconUserVerified, iconUserVerifiedUnknown, iconUserBadge, iconUserPrimary, iconUserTimed, iconVideo, iconVideoCamera, iconVideoCamera2, iconWriting, iconXBatsu, iconZones, icon2Chat2, icon2ChatQuestion2, icon2OverlaySquares2, icon3OverlaySquares2, icon3SquareCircle2, icon3Tier2, icon4Squares3, icon4Squares1, icon6Dots2, iconAddSmall2, iconAddSquares2, iconAddToCart2, iconAid2, iconAppCloud2, iconArrowDown2, iconArrowRight2, iconAttachment2, iconAudio2, iconAward2, iconBadge2, iconBag2, iconBank2, iconBattery2, iconBell2, iconBinders2, iconBolt2, iconBoltBubble2, iconBrain2, iconBriefcase2, iconBroadcast2, iconBrowser2, iconBrush2, iconCalendar3, iconCalendar1, iconCalendarAdded2, iconCart3, iconCart1, iconCash2, iconCautionFile2, iconCautionPhone2, iconCautionTriangle2, iconCenter2, iconChatBubble2, iconChatQuestion2, iconCheck2, iconCheckmark2, iconClipboard2, iconClock12, iconClock22, iconClock32, iconCloud2, iconCloudServer2, iconCode2, iconCodeBrowser2, iconCompass2, iconComputer2, iconComputerBadge2, iconComputerDownload2, iconComputerUpoad2, iconConnectedDots2, iconConnectedTogether2, iconContainerLines3, iconContainerLines22, iconCubeDotLine2, iconCubeLine2, iconCycle2, iconDesktopLaptop2, iconDevices2, iconDistribute3, iconDistribute1, iconDna2, iconDollarSign2, iconDonutCoffee2, iconDownload2, iconDrop2, iconEnterpriseBuilding2, iconEnvelope2, iconEnvelopeBug2, iconEnvelopeDoc2, iconEnvelopeShred2, iconExclamationMark2, iconEye2, iconFileApproved2, iconFileBlank2, iconFileBug2, iconFileCheck2, iconFileCloud2, iconFileCopy2, iconFileDocument2, iconFileImage2, iconFileInfographic2, iconFileInsights2, iconFileMove2, iconFileMusic2, iconFilePlus2, iconFilePresentations2, iconFileSpeed2, iconFileSpreadsheet2, iconFileText2, iconFileVideo2, iconFingerPrint2, iconFlight2, iconFolder2, iconFolderUp2, iconFolderUpload2, iconFtpFolder2, iconFunnel2, iconGear2, iconGears2, iconGraduationCap2, iconGraphIncrease2, iconGraphMeasure2, iconGroupCall2, iconHeart2, iconHome2, iconImage2, iconKeySafe2, iconLeavesGrowing2, iconLight2, iconLineGraphV22, iconLineGraph2, iconLink2, iconLock2, iconMagnifyingGlass2, iconMartini2, iconMaze2, iconMedTool2, iconMegaphone2, iconMetadata2, iconMicrophone2, iconMinus2, iconMixedMediaDownload2, iconMusicNote2, iconNotePad2, iconNumber02, iconNumber12, iconNumber22, iconNumber32, iconNumber42, iconNumber52, iconNumber62, iconNumber72, iconNumber82, iconNumber92, iconOMaru2, iconOverlay2, iconPaper2, iconPen2, iconPerson2, iconPhone3, iconPhone1, iconPhoneBrowser2, iconPieChart2, iconPieChart32, iconPin3, iconPin1, iconPlane2, iconPlay2, iconPlus2, iconPodium2, iconPoint2, iconPointCenter2, iconQuestionMark2, iconRaft2, iconRetailTag2, iconRightToForget2, iconRing2, iconRocket2, iconScale3, iconScale1, iconScan2, iconServer2, iconShakeLove2, iconSharedUsers2, iconShield2Tone2, iconShieldCheck2, iconShieldVarified2, iconSlice2, iconSlider2, iconSmbBuilding2, iconSolidArrowDown2, iconSolidArrowRight2, iconSolidCheck2, iconSolidCheckmark2, iconSolidClock12, iconSolidClock22, iconSolidCompass2, iconSolidDollarSign2, iconSolidExclamationMark2, iconSolidMinus2, iconSolidOMaru2, iconSolidPlay2, iconSolidPlus2, iconSolidQuestionMark2, iconSolidXBatsu2, iconSpeachBubble2, iconSpotlight2, iconSprayBottle2, iconSquaresScale2, iconSquaresScaleTop2, iconSquaresTogether2, iconStar2, iconStopHand2, iconStorage2, iconStorageCloud2, iconStorageKey2, iconSun2, iconTarget2, iconTeam2, iconTeamAdd2, iconThumb2, iconToggle2, iconTools2, iconTrophy2, iconTruck2, iconTwoFiles2, iconTyping2, iconTypingNow2, iconUpDown2, iconUpload2, iconUploadCloud2, iconUsb2, iconUserAlert2, iconUserVerifiedUnknown2, iconUserVerified2, iconUserBadge2, iconUserPrimary2, iconUserTimed2, iconVideo2, iconVideoCamera3, iconVideoCamera1, iconWhiteArrowRight, iconWhiteCheck, iconWhitePlus, iconWhiteStop, iconWriting2, iconXBatsu2, iconZones2, } from "../box-iconography.generated.js";
@@ -0,0 +1,2 @@
1
+ /* auto-generated by tools/iconography/generate-box-iconography.ts */
2
+ export { icon2Chat, icon2ChatQuestion, icon2OverlaySquares, icon3OverlaySquares, icon3SquareCircle, icon3Tier, icon4Squares, icon4Squares2, icon6Dots, iconAddSmall, iconAddSquares, iconAddToCart, iconAid, iconAppCloud, iconArrowDown, iconArrowRight, iconAttachment, iconAudio, iconAward, iconBadge, iconBag, iconBank, iconBattery, iconBell, iconBinders, iconBolt, iconBoltBubble, iconBrain, iconBriefcase, iconBroadcast, iconBrowser, iconBrush, iconCalendar, iconCalendar2, iconCalendarAdded, iconCart, iconCart2, iconCash, iconCautionFile, iconCautionPhone, iconCautionTriangle, iconCenter, iconChatBubble, iconChatQuestion, iconCheck, iconCheckmark, iconClipboard, iconClock1, iconClock2, iconClock3, iconCloud, iconCloudServer, iconCode, iconCodeBrowser, iconCompass, iconComputer, iconComputerBadge, iconComputerDownload, iconComputerUpoad, iconConnectedDots, iconConnectedTogether, iconContainerLines, iconContainerLines2, iconCubeDotLine, iconCubeLine, iconCycle, iconDesktopLaptop, iconDevices, iconDistribute, iconDistribute2, iconDna, iconDollarSign, iconDonutCoffee, iconDownload, iconDrop, iconEnterpriseBuilding, iconEnvelope, iconEnvelopeBug, iconEnvelopeDoc, iconEnvelopeShred, iconExclamationMark, iconEye, iconFileApproved, iconFileBlank, iconFileBug, iconFileCheck, iconFileCloud, iconFileCopy, iconFileDocument, iconFileImage, iconFileInfographic, iconFileInsights, iconFileMove, iconFileMusic, iconFilePlus, iconFilePresentations, iconFileSpeed, iconFileSpreadsheet, iconFileText, iconFileVideo, iconFingerPrint, iconFlight, iconFolder, iconFolderUp, iconFolderUpload, iconFtpFolder, iconFunnel, iconGear, iconGears, iconGraduationCap, iconGraphIncrease, iconGraphMeasure, iconGroupCall, iconHeart, iconHome, iconImage, iconKeySafe, iconLeavesGrowing, iconLight, iconLineGraphV2, iconLineGraph, iconLink, iconLock, iconMagnifyingGlass, iconMartini, iconMaze, iconMedTool, iconMegaphone, iconMetadata, iconMicrophone, iconMinus, iconMixedMediaDownload, iconMusicNote, iconNotePad, iconNumber0, iconNumber1, iconNumber2, iconNumber3, iconNumber4, iconNumber5, iconNumber6, iconNumber7, iconNumber8, iconNumber9, iconOMaru, iconOverlay, iconPaper, iconPen, iconPerson, iconPhone, iconPhone2, iconPhoneBrowser, iconPieChart, iconPieChart3, iconPin, iconPin2, iconPlane, iconPlay, iconPlus, iconPodium, iconPoint, iconPointCenter, iconQuestionMark, iconRaft, iconRetailTag, iconRightToForget, iconRing, iconRocket, iconScale, iconScale2, iconScan, iconServer, iconShakeLove, iconSharedUsers, iconShield2Tone, iconShieldCheck, iconShieldVarified, iconSlice, iconSlider, iconSmbBuilding, iconSolidArrowDown, iconSolidArrowRight, iconSolidCheck, iconSolidCheckmark, iconSolidClock1, iconSolidClock2, iconSolidCompass, iconSolidDollarSign, iconSolidExclamationMark, iconSolidMinus, iconSolidOMaru, iconSolidPlay, iconSolidPlus, iconSolidQuestionMark, iconSolidXBatsu, iconSpeachBubble, iconSpotlight, iconSprayBottle, iconSquaresScale, iconSquaresScaleTop, iconSquaresTogether, iconStar, iconStopHand, iconStorage, iconStorageCloud, iconStorageKey, iconSun, iconTarget, iconTeam, iconTeamAdd, iconThumb, iconToggle, iconTools, iconTrophy, iconTruck, iconTwoFiles, iconTyping, iconTypingNow, iconUpDown, iconUpload, iconUploadCloud, iconUsb, iconUserAlert, iconUserVerified, iconUserVerifiedUnknown, iconUserBadge, iconUserPrimary, iconUserTimed, iconVideo, iconVideoCamera, iconVideoCamera2, iconWriting, iconXBatsu, iconZones, icon2Chat2, icon2ChatQuestion2, icon2OverlaySquares2, icon3OverlaySquares2, icon3SquareCircle2, icon3Tier2, icon4Squares3, icon4Squares1, icon6Dots2, iconAddSmall2, iconAddSquares2, iconAddToCart2, iconAid2, iconAppCloud2, iconArrowDown2, iconArrowRight2, iconAttachment2, iconAudio2, iconAward2, iconBadge2, iconBag2, iconBank2, iconBattery2, iconBell2, iconBinders2, iconBolt2, iconBoltBubble2, iconBrain2, iconBriefcase2, iconBroadcast2, iconBrowser2, iconBrush2, iconCalendar3, iconCalendar1, iconCalendarAdded2, iconCart3, iconCart1, iconCash2, iconCautionFile2, iconCautionPhone2, iconCautionTriangle2, iconCenter2, iconChatBubble2, iconChatQuestion2, iconCheck2, iconCheckmark2, iconClipboard2, iconClock12, iconClock22, iconClock32, iconCloud2, iconCloudServer2, iconCode2, iconCodeBrowser2, iconCompass2, iconComputer2, iconComputerBadge2, iconComputerDownload2, iconComputerUpoad2, iconConnectedDots2, iconConnectedTogether2, iconContainerLines3, iconContainerLines22, iconCubeDotLine2, iconCubeLine2, iconCycle2, iconDesktopLaptop2, iconDevices2, iconDistribute3, iconDistribute1, iconDna2, iconDollarSign2, iconDonutCoffee2, iconDownload2, iconDrop2, iconEnterpriseBuilding2, iconEnvelope2, iconEnvelopeBug2, iconEnvelopeDoc2, iconEnvelopeShred2, iconExclamationMark2, iconEye2, iconFileApproved2, iconFileBlank2, iconFileBug2, iconFileCheck2, iconFileCloud2, iconFileCopy2, iconFileDocument2, iconFileImage2, iconFileInfographic2, iconFileInsights2, iconFileMove2, iconFileMusic2, iconFilePlus2, iconFilePresentations2, iconFileSpeed2, iconFileSpreadsheet2, iconFileText2, iconFileVideo2, iconFingerPrint2, iconFlight2, iconFolder2, iconFolderUp2, iconFolderUpload2, iconFtpFolder2, iconFunnel2, iconGear2, iconGears2, iconGraduationCap2, iconGraphIncrease2, iconGraphMeasure2, iconGroupCall2, iconHeart2, iconHome2, iconImage2, iconKeySafe2, iconLeavesGrowing2, iconLight2, iconLineGraphV22, iconLineGraph2, iconLink2, iconLock2, iconMagnifyingGlass2, iconMartini2, iconMaze2, iconMedTool2, iconMegaphone2, iconMetadata2, iconMicrophone2, iconMinus2, iconMixedMediaDownload2, iconMusicNote2, iconNotePad2, iconNumber02, iconNumber12, iconNumber22, iconNumber32, iconNumber42, iconNumber52, iconNumber62, iconNumber72, iconNumber82, iconNumber92, iconOMaru2, iconOverlay2, iconPaper2, iconPen2, iconPerson2, iconPhone3, iconPhone1, iconPhoneBrowser2, iconPieChart2, iconPieChart32, iconPin3, iconPin1, iconPlane2, iconPlay2, iconPlus2, iconPodium2, iconPoint2, iconPointCenter2, iconQuestionMark2, iconRaft2, iconRetailTag2, iconRightToForget2, iconRing2, iconRocket2, iconScale3, iconScale1, iconScan2, iconServer2, iconShakeLove2, iconSharedUsers2, iconShield2Tone2, iconShieldCheck2, iconShieldVarified2, iconSlice2, iconSlider2, iconSmbBuilding2, iconSolidArrowDown2, iconSolidArrowRight2, iconSolidCheck2, iconSolidCheckmark2, iconSolidClock12, iconSolidClock22, iconSolidCompass2, iconSolidDollarSign2, iconSolidExclamationMark2, iconSolidMinus2, iconSolidOMaru2, iconSolidPlay2, iconSolidPlus2, iconSolidQuestionMark2, iconSolidXBatsu2, iconSpeachBubble2, iconSpotlight2, iconSprayBottle2, iconSquaresScale2, iconSquaresScaleTop2, iconSquaresTogether2, iconStar2, iconStopHand2, iconStorage2, iconStorageCloud2, iconStorageKey2, iconSun2, iconTarget2, iconTeam2, iconTeamAdd2, iconThumb2, iconToggle2, iconTools2, iconTrophy2, iconTruck2, iconTwoFiles2, iconTyping2, iconTypingNow2, iconUpDown2, iconUpload2, iconUploadCloud2, iconUsb2, iconUserAlert2, iconUserVerifiedUnknown2, iconUserVerified2, iconUserBadge2, iconUserPrimary2, iconUserTimed2, iconVideo2, iconVideoCamera3, iconVideoCamera1, iconWhiteArrowRight, iconWhiteCheck, iconWhitePlus, iconWhiteStop, iconWriting2, iconXBatsu2, iconZones2, } from "../box-iconography.generated.js";
@@ -0,0 +1 @@
1
+ {"type":"module","sideEffects":false}
@@ -91,6 +91,13 @@ export const resolvePosition = (anchor, floating, viewport, options = {}) => {
91
91
  if (shift)
92
92
  y = clamp(y, padding, viewport.height - floating.height - padding);
93
93
  }
94
+ // On a narrow viewport neither side may fit (a right popover can flip to a
95
+ // still-offscreen left position). Keep the surface reachable after flipping.
96
+ // Explicit flip/shift opt-outs retain the requested overflow behavior.
97
+ if (flip && shift) {
98
+ x = clamp(x, padding, viewport.width - floating.width - padding);
99
+ y = clamp(y, padding, viewport.height - floating.height - padding);
100
+ }
94
101
  return { x, y, side, align };
95
102
  };
96
103
  /** Parse a placement string like `"bottom-start"` / `"top"` / `"right-center"`. */
@@ -1,4 +1,4 @@
1
- export type StatusKind = "pending" | "active" | "done" | "warning" | "failed" | "skipped";
1
+ export type StatusKind = "pending" | "active" | "done" | "warning" | "failed" | "skipped" | "approved" | "rejected";
2
2
  export declare const toStatusKind: (status: string) => StatusKind;
3
3
  export declare const statusLabel: (kind: StatusKind) => string;
4
4
  /** Decorative markup. Pair with visible words or the StatusIcon component. */
@@ -1,21 +1,23 @@
1
1
  const mappings = {
2
2
  pending: "pending", info: "pending", active: "active", running: "active", in_progress: "active",
3
- done: "done", succeeded: "done", completed: "done", pass: "done", approved: "done",
3
+ done: "done", succeeded: "done", completed: "done", pass: "done", approved: "approved",
4
4
  warning: "warning", warn: "warning", failed: "failed", fail: "failed", error: "failed",
5
- skipped: "skipped", rejected: "skipped",
5
+ skipped: "skipped", rejected: "rejected",
6
6
  };
7
7
  export const toStatusKind = (status) => mappings[status] ?? "pending";
8
8
  export const statusLabel = (kind) => ({
9
9
  pending: "Pending", active: "In progress", done: "Done", warning: "Done with a warning",
10
10
  failed: "Failed", skipped: "Skipped",
11
+ approved: "Approved", rejected: "Rejected",
11
12
  })[kind];
12
13
  /** Decorative markup. Pair with visible words or the StatusIcon component. */
13
14
  export const boeStatusGlyph = (kind) => {
14
15
  const state = toStatusKind(kind);
15
- const path = state === "done" ? '<path d="m4 8 3 3 5-6"/>'
16
- : state === "failed" ? '<path d="m5 5 6 6m0-6-6 6"/>'
17
- : state === "warning" ? '<path d="M5 8h6"/>'
18
- : state === "active" ? '<path d="M8 1a7 7 0 0 1 7 7"/>' : "";
16
+ const path = state === "done" || state === "approved" ? '<path d="m4 8 3 3 5-6"/>'
17
+ : state === "rejected" ? '<path d="M4 12 12 4"/>'
18
+ : state === "failed" ? '<path d="m5 5 6 6m0-6-6 6"/>'
19
+ : state === "warning" ? '<path d="M5 8h6"/>'
20
+ : state === "active" ? '<path d="M8 1a7 7 0 0 1 7 7"/>' : "";
19
21
  return `<svg class="boe-status" data-kind="${state}" aria-hidden="true" viewBox="0 0 16 16" width="16" height="16"><circle cx="8" cy="8" r="7"/>${path}</svg>`;
20
22
  };
21
23
  export const boeStatusStyles = `
@@ -36,7 +36,10 @@ export const boxDarkDesignSystem = {
36
36
  TextTextDanger: "#ff8a9b",
37
37
  TextTextSecondary: "#bcbcbc",
38
38
  TextTextPlaceholder: "#909090",
39
- TextTextOnBrand: "#ffffff",
39
+ TextTextOnBrand: "#101010",
40
+ TextStatusTextSuccess: "#2ed58f",
41
+ TextStatusTextWarning: "#ffc447",
42
+ TextStatusTextError: "#ff8a9b",
40
43
  StrokeStroke: "#3a3a3a",
41
44
  StrokeStrokeHover: "#4e4e4e",
42
45
  },
@@ -34,6 +34,9 @@ export const boxDefaultDesignSystem = {
34
34
  TextTextPlaceholder: "#909090",
35
35
  TextTextOnBrand: "#ffffff",
36
36
  TextTextDanger: "#d5324e",
37
+ TextStatusTextSuccess: "#187657",
38
+ TextStatusTextWarning: "#805600",
39
+ TextStatusTextError: "#b92340",
37
40
  StrokeStroke: "#e8e8e8",
38
41
  StrokeStrokeHover: "#bcbcbc",
39
42
  },
@@ -26,6 +26,9 @@ export declare const SEMANTIC_TOKEN_ALIASES: {
26
26
  readonly textPlaceholder: "TextTextPlaceholder";
27
27
  readonly textOnBrand: "TextTextOnBrand";
28
28
  readonly textDanger: "TextTextDanger";
29
+ readonly textStatusSuccess: "TextStatusTextSuccess";
30
+ readonly textStatusWarning: "TextStatusTextWarning";
31
+ readonly textStatusError: "TextStatusTextError";
29
32
  readonly borderDefault: "StrokeStroke";
30
33
  readonly borderHover: "StrokeStrokeHover";
31
34
  };
@@ -28,6 +28,9 @@ export const SEMANTIC_TOKEN_ALIASES = {
28
28
  textPlaceholder: "TextTextPlaceholder",
29
29
  textOnBrand: "TextTextOnBrand",
30
30
  textDanger: "TextTextDanger",
31
+ textStatusSuccess: "TextStatusTextSuccess",
32
+ textStatusWarning: "TextStatusTextWarning",
33
+ textStatusError: "TextStatusTextError",
31
34
  borderDefault: "StrokeStroke",
32
35
  borderHover: "StrokeStrokeHover",
33
36
  };
@@ -29,6 +29,9 @@ export interface SemanticDesignTokenMap {
29
29
  textPlaceholder?: string;
30
30
  textOnBrand?: string;
31
31
  textDanger?: string;
32
+ textStatusSuccess?: string;
33
+ textStatusWarning?: string;
34
+ textStatusError?: string;
32
35
  borderDefault?: string;
33
36
  borderHover?: string;
34
37
  }
package/dist/index.d.ts CHANGED
@@ -144,3 +144,5 @@ export * from "./patterns/agent-workspace/index.js";
144
144
  export * from "./patterns/call-console/index.js";
145
145
  export * from "./foundations/status/index.js";
146
146
  export * from "./patterns/run/progress.js";
147
+ export * from "./patterns/undo/index.js";
148
+ export * from "./patterns/flow-builder/index.js";
package/dist/index.js CHANGED
@@ -158,3 +158,5 @@ export * from "./patterns/agent-workspace/index.js";
158
158
  export * from "./patterns/call-console/index.js";
159
159
  export * from "./foundations/status/index.js";
160
160
  export * from "./patterns/run/progress.js";
161
+ export * from "./patterns/undo/index.js";
162
+ export * from "./patterns/flow-builder/index.js";
@@ -1,6 +1,7 @@
1
1
  import { AgentChatController } from "./controller.js";
2
2
  import { isSafeHref } from "../internal/safe-href.js";
3
3
  import { BaseElement } from "../../core/index.js";
4
+ import { announce } from "../../foundations/a11y/index.js";
4
5
  import { boeMotionDuration, boeMotionEasing } from "../../foundations/motion/index.js";
5
6
  import { boePanel, boeRadius } from "../../foundations/geometry/index.js";
6
7
  import { ScrollPinController, boeEntranceKeyframes, boeReducedMotionPolicy } from "../../foundations/motion/index.js";
@@ -242,7 +243,7 @@ const elementStyles = `
242
243
  gap: 0.4rem;
243
244
  }
244
245
 
245
- [part="proposal-action"] {
246
+ [part="proposal-action"], [part="option"], [part="retry"] {
246
247
  appearance: none;
247
248
  font: inherit;
248
249
  font-size: 0.78rem;
@@ -256,7 +257,9 @@ const elementStyles = `
256
257
  transition: background ${boeMotionDuration.interactive} ${boeMotionEasing.standard};
257
258
  }
258
259
 
259
- [part="proposal-action"]:hover {
260
+ [part="option"], [part="retry"] { justify-self: start; min-height: 32px; text-align: left; }
261
+
262
+ [part="proposal-action"]:hover, [part="option"]:hover, [part="retry"]:hover {
260
263
  background: var(--boe-token-surface-surface-hover, #f4f4f4);
261
264
  }
262
265
 
@@ -270,7 +273,7 @@ const elementStyles = `
270
273
  color: color-mix(in srgb, var(--boe-token-surface-status-surface-error, #ed3757) 74%, black 26%);
271
274
  }
272
275
 
273
- [part="proposal-action"]:focus-visible {
276
+ [part="proposal-action"]:focus-visible, [part="option"]:focus-visible, [part="retry"]:focus-visible {
274
277
  outline: none;
275
278
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 18%, transparent);
276
279
  }
@@ -563,6 +566,11 @@ export class AgentChat extends BaseElement {
563
566
  ["actionResolved", "action-resolved"],
564
567
  ];
565
568
  this.unsubscribeFns = events.map(([eventName, domEventName]) => controller.subscribe(eventName, payload => {
569
+ if (eventName === "actionResolved") {
570
+ const { proposal, decision } = payload;
571
+ const failed = proposal.outcome === "failed";
572
+ announce(`${proposal.title}: ${failed ? "Approved, but did not complete" : decision === "approved" ? "Approved" : "Rejected"}${proposal.note ? `. ${proposal.note}` : ""}`, failed ? "assertive" : "polite", this.ownerDocument);
573
+ }
566
574
  this.dispatchEvent(new CustomEvent(domEventName, {
567
575
  bubbles: true,
568
576
  composed: true,
@@ -598,7 +606,7 @@ export class AgentChat extends BaseElement {
598
606
  .map(proposal => {
599
607
  if (proposal.decision) {
600
608
  const failed = proposal.outcome === "failed";
601
- return `<div part="proposal" tabindex="-1" id="${this.proposalAnchor(message.id, proposal.id)}" data-proposal-id="${escapeHtml(proposal.id)}" data-decision="${escapeHtml(proposal.decision)}" ${failed ? 'role="alert"' : ""}>
609
+ return `<div part="proposal" aria-live="off" tabindex="-1" id="${this.proposalAnchor(message.id, proposal.id)}" data-proposal-id="${escapeHtml(proposal.id)}" data-decision="${escapeHtml(proposal.decision)}" ${failed ? 'role="alert"' : ""}>
602
610
  <span part="decision">${boeStatusGlyph(failed ? "failed" : proposal.decision === "approved" ? "done" : "skipped")}${proposal.decision === "approved" ? failed ? "Approved · didn't complete" : "Approved" : "Rejected"} · <span part="proposal-title">${escapeHtml(proposal.title)}</span></span>
603
611
  ${proposal.note ? `<p part="proposal-note">${escapeHtml(proposal.note)}</p>` : ""}
604
612
  ${failed ? `<button part="retry" data-message-id="${escapeHtml(message.id)}">Try again</button>` : ""}</div>`;
@@ -6,6 +6,8 @@ export declare class CallConsole extends BaseElement {
6
6
  private model;
7
7
  private unsubscribe?;
8
8
  private selected;
9
+ private clearing;
10
+ private rowSignatures;
9
11
  get callController(): CallConsoleController | null;
10
12
  set callController(value: CallConsoleController | null);
11
13
  private listen;
@@ -13,5 +15,6 @@ export declare class CallConsole extends BaseElement {
13
15
  disconnectedCallback(): void;
14
16
  protected renderTemplate(): void;
15
17
  protected setupListeners(): void;
18
+ private get filters();
16
19
  protected update(): void;
17
20
  }
@@ -1,5 +1,9 @@
1
1
  import { BaseElement } from "../../core/element.js";
2
2
  import { SplitView } from "../../components/layout/split-view.js";
3
+ import { Button } from "../../components/actions/button.js";
4
+ import { CodeBlock } from "../../components/output/code-block.js";
5
+ import { boeControl, boeRadius } from "../../foundations/geometry/index.js";
6
+ import { boeStatusGlyph, boeStatusStyles } from "../../foundations/status/index.js";
3
7
  import { boeEntrance, boeEntranceKeyframes, boeReducedMotionPolicy } from "../../foundations/motion/index.js";
4
8
  import { callFailed, callStatus, formatCallHttp } from "./types.js";
5
9
  const DEFAULT_TAG_NAME = "box-call-console";
@@ -29,6 +33,8 @@ export class CallConsole extends BaseElement {
29
33
  model = null;
30
34
  unsubscribe;
31
35
  selected = "";
36
+ clearing = false;
37
+ rowSignatures = new WeakMap();
32
38
  get callController() { return this.model; }
33
39
  set callController(value) { this.unsubscribe?.(); this.model = value; if (this.isConnected)
34
40
  this.listen(); if (this.isRendered)
@@ -38,21 +44,73 @@ export class CallConsole extends BaseElement {
38
44
  disconnectedCallback() { this.unsubscribe?.(); }
39
45
  renderTemplate() {
40
46
  SplitView.register();
47
+ Button.register();
48
+ CodeBlock.register();
41
49
  this.shadowRoot.innerHTML = `<style>:host{display:block;min-width:0;color:var(--boe-token-text-text,#222);font:inherit}:host([hidden]){display:none !important}
42
- header{display:flex;gap:.75rem;flex-wrap:wrap;align-items:center} pre{white-space:pre-wrap;overflow-wrap:anywhere;font-size:.8rem;max-height:30rem;overflow:auto}
43
- button,select{font:inherit;color:inherit;background:var(--boe-token-surface-surface,#fff);border:1px solid var(--boe-token-stroke-stroke,#ddd);border-radius:5px;padding:.4rem;cursor:pointer}
44
- [part="call"]{width:100%;display:grid;grid-template-columns:5rem 5rem minmax(0,1fr) 4rem;text-align:left;gap:.5rem;margin:.4rem 0;${boeEntrance()}}
45
- #list{container-type:inline-size} [part="call"] span{min-width:0;overflow-wrap:anywhere}
46
- @container(max-width:420px){[part="call"]{grid-template-columns:minmax(0,1fr) auto}[part="call"] span:nth-child(even){text-align:right}}
47
- [aria-current="true"]{background:var(--boe-token-surface-surface-secondary,#f5f5f5)} [part="skeleton"]{height:2rem;margin:.5rem;background:var(--boe-token-surface-surface-secondary,#eee)}
48
- ${boeEntranceKeyframes}${boeReducedMotionPolicy}</style>
49
- <header><span id="connection" role="status"></span><label>Service <select id="service"><option value="">All</option></select></label><label><input type="checkbox" id="errors"> Errors only</label><button id="clear">Clear</button><span id="copy-status" role="status"></span></header>
50
- <box-split-view resizable ratio="0.4"><div slot="primary" id="list"></div><div><button data-copy="request">Copy request</button><button data-copy="response">Copy response</button><button data-copy="both">Copy both</button><h3>Request <button data-copy="request" aria-label="Copy request code">⧉</button></h3><pre id="request"></pre><h3>Response <button data-copy="response" aria-label="Copy response code">⧉</button></h3><pre id="response"></pre></div></box-split-view>`;
50
+ :host{container-type:inline-size;font-size:14px;line-height:1.5}
51
+ *{box-sizing:border-box} [hidden]{display:none!important}
52
+ [part="console"]{border:1px solid var(--boe-token-stroke-stroke,#ddd);border-radius:${boeRadius.large};background:var(--boe-token-surface-surface,#fff);overflow:hidden}
53
+ header,.filters,.pane-heading{display:flex;gap:.75rem;flex-wrap:wrap;align-items:center;padding:1rem}
54
+ header{justify-content:space-between} h2,h3,p{margin:0} h2{font-size:16px} h3{font-size:14px} small,.muted{color:var(--boe-token-text-text-secondary,#666);font-size:12px}
55
+ #connection{display:inline-flex;align-items:center;gap:.4rem;font-size:12px}
56
+ .filters{border-block:1px solid var(--boe-token-stroke-stroke,#ddd);background:var(--boe-token-surface-surface-secondary,#f7f7f7);padding:.75rem 1rem}
57
+ label{display:flex;align-items:center;gap:.5rem;font-size:13px} .search{flex:1;min-width:10rem} .search input{width:100%}
58
+ input[type="search"],select{min-height:${boeControl.height};border:1px solid var(--boe-token-stroke-stroke,#ccc);border-radius:${boeRadius.control};background:var(--boe-token-surface-surface,#fff);color:inherit;font:inherit;padding:.35rem .65rem;min-width:0}
59
+ input[type="checkbox"]{accent-color:var(--boe-token-surface-surface-brand,#0061d5)}
60
+ button:focus-visible,input:focus-visible,select:focus-visible{outline:2px solid var(--boe-token-surface-surface-brand,#0061d5);outline-offset:-2px}
61
+ .pane-heading{justify-content:space-between;padding:.75rem 1rem;border-bottom:1px solid var(--boe-token-stroke-stroke,#ddd)}
62
+ #list{overflow:auto;max-height:34rem;min-height:20rem;padding:.35rem}
63
+ [part="call"]{font:inherit;color:inherit;border:1px solid transparent;background:transparent;border-radius:${boeRadius.med};width:100%;display:grid;grid-template-columns:minmax(0,1fr) auto;text-align:left;gap:.3rem .75rem;padding:.75rem;cursor:pointer;${boeEntrance()}}
64
+ [part="call"]:hover{background:var(--boe-token-surface-surface-secondary,#f5f5f5)}
65
+ [part="call"][aria-current="true"]{background:var(--boe-token-surface-item-surface-selected,#eaf3ff);border-color:var(--boe-token-surface-surface-brand,#0061d5)}
66
+ [part="call"] span{min-width:0;overflow-wrap:anywhere} [part="call"] .state{display:flex;gap:.4rem;align-items:center;font-size:12px} [part="call"] .summary{font-weight:600} [part="call"] .duration{text-align:right;font-variant-numeric:tabular-nums;font-size:12px}
67
+ #inspector{padding:1rem;min-width:0;max-height:38rem;overflow:auto} #summary{font-size:15px;overflow-wrap:anywhere} #metadata{margin:.25rem 0 1rem;overflow-wrap:anywhere} .code-heading{margin:1rem 0 .5rem}
68
+ #outcome{padding:.6rem .75rem;margin:.75rem 0;border-radius:${boeRadius.med};background:var(--boe-token-surface-surface-secondary,#f5f5f5);overflow-wrap:anywhere}
69
+ #copy-status{font-size:12px;color:var(--boe-token-text-text-secondary,#666)}
70
+ #list-empty,#detail-empty{padding:2.5rem 1rem;text-align:center;color:var(--boe-token-text-text-secondary,#666)}
71
+ [part="skeleton"]{height:3rem;margin:.5rem;background:var(--boe-token-surface-surface-secondary,#eee);border-radius:${boeRadius.med}}
72
+ box-code-block::part(pre){max-height:18rem;overflow:auto}
73
+ @container(max-width:640px){box-split-view::part(split-view){grid-template-columns:minmax(0,1fr)!important}box-split-view::part(separator){display:none}#list{min-height:0;max-height:17rem}#inspector{border-top:1px solid var(--boe-token-stroke-stroke,#ddd)}.search{flex-basis:100%}}
74
+ ${boeEntranceKeyframes}${boeStatusStyles}${boeReducedMotionPolicy}</style>
75
+ <section part="console" aria-label="API call console">
76
+ <header><div><h2>API calls</h2><small>Inspect requests, responses, and tool outcomes</small></div><span id="connection" role="status"></span><box-button id="retry" label="Reconnect" tone="neutral" hidden></box-button></header>
77
+ <div class="filters"><label class="search">Search <input id="search" type="search" placeholder="Method, URL, or summary"></label><label>Service <select id="service" aria-label="Service"><option value="">All services</option></select></label><label><input type="checkbox" id="errors"> Errors only</label><box-button id="clear" label="Clear calls" tone="neutral"></box-button></div>
78
+ <box-split-view resizable ratio="0.42" label="Calls and request inspector"><section slot="primary" aria-label="Calls"><div class="pane-heading"><h3>Requests</h3><span id="count" class="muted" role="status"></span></div><div id="list"></div><p id="list-empty" role="status" hidden></p></section><section aria-label="Request inspector"><div class="pane-heading"><h3>Inspector</h3><box-button data-copy="both" label="Copy both" tone="neutral"></box-button><span id="copy-status" role="status"></span></div><p id="detail-empty">Select a request to inspect its details.</p><div id="inspector" hidden><h3 id="summary"></h3><p id="metadata" class="muted"></p><p id="outcome" role="status" hidden></p><h3 class="code-heading">Request</h3><box-code-block id="request" copy-label="Copy request" wrap></box-code-block><h3 class="code-heading">Response</h3><box-code-block id="response" copy-label="Copy response" wrap></box-code-block></div></section></box-split-view></section>`;
51
79
  }
52
80
  setupListeners() {
53
- this.shadowRoot.querySelector("#service").addEventListener("change", () => this.update());
54
- this.shadowRoot.querySelector("#errors").addEventListener("change", () => this.update());
55
- this.shadowRoot.querySelector("#clear").addEventListener("click", () => { void this.model?.clear().catch(() => { this.shadowRoot.querySelector("#copy-status").textContent = "Unable to clear calls"; }); });
81
+ this.shadowRoot.querySelector("#list").addEventListener("keydown", event => {
82
+ const e = event;
83
+ const rows = Array.from(this.shadowRoot.querySelectorAll('[part="call"]'));
84
+ const current = rows.indexOf(e.target);
85
+ if (current < 0 || !["ArrowDown", "ArrowUp", "Home", "End"].includes(e.key))
86
+ return;
87
+ e.preventDefault();
88
+ const next = e.key === "Home" ? 0 : e.key === "End" ? rows.length - 1 : (current + (e.key === "ArrowDown" ? 1 : -1) + rows.length) % rows.length;
89
+ rows[next].focus();
90
+ rows[next].click();
91
+ });
92
+ const filter = () => { this.update(); this.dispatchEvent(new CustomEvent("filters-changed", { bubbles: true, composed: true, detail: this.filters })); };
93
+ this.shadowRoot.querySelector("#service").addEventListener("change", filter);
94
+ this.shadowRoot.querySelector("#errors").addEventListener("change", filter);
95
+ this.shadowRoot.querySelector("#search").addEventListener("input", filter);
96
+ this.shadowRoot.querySelector("#retry").addEventListener("click", () => { void this.model?.connect(); });
97
+ this.shadowRoot.querySelector("#clear").addEventListener("click", async () => {
98
+ if (!this.model || this.clearing)
99
+ return;
100
+ this.clearing = true;
101
+ this.update();
102
+ try {
103
+ await this.model.clear();
104
+ this.dispatchEvent(new CustomEvent("calls-cleared", { bubbles: true, composed: true }));
105
+ }
106
+ catch {
107
+ this.shadowRoot.querySelector("#copy-status").textContent = "Unable to clear calls. Try again.";
108
+ }
109
+ finally {
110
+ this.clearing = false;
111
+ this.update();
112
+ }
113
+ });
56
114
  this.shadowRoot.querySelectorAll("[data-copy]").forEach(b => b.addEventListener("click", async () => {
57
115
  const entry = this.model?.entries.find(e => e.id === this.selected);
58
116
  if (!entry)
@@ -61,9 +119,19 @@ export class CallConsole extends BaseElement {
61
119
  this.shadowRoot.querySelector("#copy-status").textContent = ok ? "Copied" : "Copy unavailable; select the text to copy";
62
120
  }));
63
121
  }
122
+ get filters() {
123
+ return { service: this.shadowRoot.querySelector("#service").value, errorsOnly: this.shadowRoot.querySelector("#errors").checked, query: this.shadowRoot.querySelector("#search").value };
124
+ }
64
125
  update() {
126
+ if (!this.isRendered)
127
+ return;
65
128
  const state = this.model?.connection ?? "connecting";
66
- this.shadowRoot.querySelector("#connection").textContent = state === "unavailable" ? "Restart the agent: /calls is unavailable" : state;
129
+ const connection = this.shadowRoot.querySelector("#connection");
130
+ connection.textContent = this.model ? ({ live: "Live", connecting: "Connecting…", reconnecting: "Reconnecting…", unavailable: "Endpoint unavailable" }[state]) : "Not connected";
131
+ this.shadowRoot.querySelector("#retry").hidden = !this.model || state === "live" || state === "connecting";
132
+ const clear = this.shadowRoot.querySelector("#clear");
133
+ clear.disabled = this.clearing || !this.model?.entries.length;
134
+ clear.label = this.clearing ? "Clearing…" : "Clear calls";
67
135
  const service = this.shadowRoot.querySelector("#service");
68
136
  const services = [...new Set(this.model?.entries.map(e => e.service) ?? [])];
69
137
  for (const name of services)
@@ -75,33 +143,59 @@ export class CallConsole extends BaseElement {
75
143
  }
76
144
  const errors = this.shadowRoot.querySelector("#errors").checked;
77
145
  const entries = this.model?.entries.filter(e => (!service.value || e.service === service.value) && (!errors || callFailed(e))) ?? [];
146
+ const query = this.filters.query.trim().toLowerCase();
147
+ const filtered = entries.filter(e => `${e.method} ${e.url} ${e.summary} ${e.service}`.toLowerCase().includes(query));
148
+ entries.splice(0, entries.length, ...filtered);
78
149
  if (!entries.some(e => e.id === this.selected))
79
150
  this.selected = entries[0]?.id ?? "";
80
151
  const list = this.shadowRoot.querySelector("#list");
81
152
  const focused = this.shadowRoot.activeElement?.getAttribute("data-call-id");
82
153
  list.querySelectorAll('[part="skeleton"]').forEach(el => el.remove());
83
154
  const existing = new Map(Array.from(list.children).map(el => [el.getAttribute("data-call-id"), el]));
84
- for (const entry of entries) {
155
+ for (const [index, entry] of entries.entries()) {
85
156
  let button = existing.get(entry.id);
86
157
  existing.delete(entry.id);
87
158
  if (!button) {
88
159
  button = document.createElement("button");
160
+ button.type = "button";
89
161
  button.setAttribute("part", "call");
90
162
  button.dataset.callId = entry.id;
91
- button.addEventListener("click", () => { this.selected = entry.id; this.update(); });
163
+ button.addEventListener("click", () => { this.selected = entry.id; this.update(); this.dispatchEvent(new CustomEvent("call-selected", { bubbles: true, composed: true, detail: { id: entry.id } })); });
92
164
  }
93
165
  button.setAttribute("aria-current", String(entry.id === this.selected));
94
- button.replaceChildren(...[callStatus(entry), entry.service, entry.summary, `${Math.round(entry.durationMs)} ms`].map(text => { const span = document.createElement("span"); span.textContent = text; return span; }));
95
- list.append(button);
166
+ const labels = [callStatus(entry), entry.service, entry.summary, entry.pending ? "In progress" : `${Math.round(entry.durationMs)} ms`];
167
+ const signature = JSON.stringify(labels);
168
+ if (this.rowSignatures.get(button) !== signature) {
169
+ button.replaceChildren(...labels.map((text, index) => { const span = document.createElement("span"); span.textContent = text; span.className = ["state", "muted", "summary", "duration"][index]; if (index === 0) {
170
+ const icon = document.createElement("span");
171
+ icon.innerHTML = boeStatusGlyph(entry.pending ? "active" : callFailed(entry) ? "failed" : entry.expected ? "skipped" : "done");
172
+ span.prepend(icon);
173
+ } return span; }));
174
+ this.rowSignatures.set(button, signature);
175
+ }
176
+ if (list.children[index] !== button)
177
+ list.insertBefore(button, list.children[index] ?? null);
96
178
  if (entry.id === focused)
97
179
  button.focus({ preventScroll: true });
98
180
  }
99
181
  existing.forEach(el => el.remove());
100
- if (!entries.length && state === "connecting")
182
+ if (!entries.length && state === "connecting" && this.model)
101
183
  list.innerHTML = '<div part="skeleton"></div><div part="skeleton"></div><div part="skeleton"></div>';
184
+ this.shadowRoot.querySelector("#count").textContent = `${entries.length} of ${this.model?.entries.length ?? 0}`;
185
+ const empty = this.shadowRoot.querySelector("#list-empty");
186
+ empty.hidden = entries.length > 0 || !!this.model && state === "connecting";
187
+ empty.textContent = !this.model ? "Connect a call controller to inspect requests." : state === "unavailable" ? "The /calls endpoint is unavailable. Start the agent, then reconnect." : state === "reconnecting" ? "Connection interrupted. Reconnect to load calls." : this.model.entries.length ? "No requests match these filters." : "No calls yet. New requests will appear here.";
102
188
  const entry = entries.find(e => e.id === this.selected);
103
- this.shadowRoot.querySelector("#request").textContent = entry ? formatCallHttp(entry, "request") : "Select a call";
104
- this.shadowRoot.querySelector("#response").textContent = entry ? formatCallHttp(entry, "response") : "";
189
+ this.shadowRoot.querySelector('[data-copy="both"]').disabled = !entry;
190
+ this.shadowRoot.querySelector("#inspector").hidden = !entry;
191
+ this.shadowRoot.querySelector("#detail-empty").hidden = !!entry;
192
+ this.shadowRoot.querySelector("#summary").textContent = entry?.summary ?? "";
193
+ this.shadowRoot.querySelector("#metadata").textContent = entry ? `${entry.service} · ${entry.method} · ${entry.pending ? "In progress" : `${entry.durationMs} ms`} · ${new Date(entry.startedAt).toLocaleTimeString()}` : "";
194
+ const outcome = this.shadowRoot.querySelector("#outcome");
195
+ outcome.hidden = !entry || !(entry.error || entry.rpcError || entry.expected || entry.pending);
196
+ outcome.textContent = entry?.rpcError ? `Tool failed despite HTTP ${entry.status}: ${entry.rpcError}` : entry?.error ?? entry?.expected ?? (entry?.pending ? "Waiting for the response…" : "");
197
+ this.shadowRoot.querySelector("#request").code = entry ? formatCallHttp(entry, "request") : "";
198
+ this.shadowRoot.querySelector("#response").code = entry ? entry.pending ? "Response pending" : formatCallHttp(entry, "response") : "";
105
199
  }
106
200
  }
107
201
  CallConsole.register();
@@ -206,6 +206,8 @@ export class ExplorerActionMenu extends BaseElement {
206
206
  if (this.itemIdValue && actionId) {
207
207
  const item = this.controllerValue?.getState().items.find(entry => entry.id === this.itemIdValue) ?? null;
208
208
  const action = this.controllerValue?.getItemActions(this.itemIdValue).find(entry => entry.id === actionId) ?? null;
209
+ if (!action || action.disabled)
210
+ return;
209
211
  this.controllerValue?.invokeItemAction(this.itemIdValue, actionId);
210
212
  if (item && action) {
211
213
  this.dispatchEvent(new CustomEvent(toKebabCase("itemActionInvoked"), {
@@ -249,7 +251,7 @@ export class ExplorerActionMenu extends BaseElement {
249
251
  });
250
252
  return;
251
253
  }
252
- const menuItems = Array.from(this.shadowRoot.querySelectorAll('[part="menu-item"]'));
254
+ const menuItems = Array.from(this.shadowRoot.querySelectorAll('[part="menu-item"]:not(:disabled)'));
253
255
  handleRovingKeydown(keyboardEvent, menuItems, { orientation: "vertical" });
254
256
  });
255
257
  }
@@ -269,7 +271,7 @@ export class ExplorerActionMenu extends BaseElement {
269
271
  }
270
272
  const menuMarkup = this.open
271
273
  ? `<div id="${this.menuId}" part="menu" role="menu">${actions
272
- .map(action => `<button type="button" part="menu-item" role="menuitem" data-action-id="${escapeHtml(action.id)}">${escapeHtml(action.label)}</button>`)
274
+ .map(action => `<button type="button" part="menu-item" role="menuitem" data-action-id="${escapeHtml(action.id)}" ${action.disabled ? 'disabled aria-disabled="true" tabindex="-1"' : ''}>${escapeHtml(action.label)}</button>`)
273
275
  .join("")}</div>`
274
276
  : "";
275
277
  this.hostEl.innerHTML = `
@@ -286,12 +288,12 @@ export class ExplorerActionMenu extends BaseElement {
286
288
  </div>
287
289
  `;
288
290
  if (this.open) {
289
- const menuItems = Array.from(this.shadowRoot?.querySelectorAll('[part="menu-item"]') ?? []);
291
+ const menuItems = Array.from(this.shadowRoot?.querySelectorAll('[part="menu-item"]:not(:disabled)') ?? []);
290
292
  applyRovingTabindex(menuItems, 0);
291
293
  }
292
294
  if (justOpened) {
293
295
  queueMicrotask(() => {
294
- this.shadowRoot?.querySelector('[part="menu-item"]')?.focus();
296
+ this.shadowRoot?.querySelector('[part="menu-item"]:not(:disabled)')?.focus();
295
297
  });
296
298
  return;
297
299
  }
@@ -302,7 +304,7 @@ export class ExplorerActionMenu extends BaseElement {
302
304
  });
303
305
  return;
304
306
  }
305
- if (focusedTrigger) {
307
+ if (focusedTrigger || (focusedActionId && !this.open)) {
306
308
  queueMicrotask(() => {
307
309
  this.shadowRoot?.querySelector('[part="trigger"]')?.focus();
308
310
  });
@@ -9,6 +9,7 @@ const toKebabCase = (value) => value.replace(/[A-Z]/g, letter => `-${letter.toLo
9
9
  const elementStyles = `
10
10
  :host {
11
11
  display: block;
12
+ min-inline-size: 0;
12
13
  color: inherit;
13
14
  font: inherit;
14
15
  }
@@ -33,14 +34,15 @@ const elementStyles = `
33
34
 
34
35
  [part="search"] {
35
36
  flex: 1 1 14rem;
36
- min-width: 12rem;
37
+ min-width: 0;
38
+ max-width: 100%;
37
39
  }
38
40
 
39
41
  [part="status"],
40
42
  [part="selection-count"] {
41
43
  color: var(--boe-token-text-text-secondary, #6f6f6f);
42
44
  font-size: 0.85rem;
43
- white-space: nowrap;
45
+ overflow-wrap: anywhere;
44
46
  padding-bottom: 0.45rem;
45
47
  }
46
48
 
@@ -54,6 +56,8 @@ const elementStyles = `
54
56
 
55
57
  [part="refresh"],
56
58
  [part="clear-selection"] {
59
+ max-inline-size: 100%;
60
+ overflow-wrap: anywhere;
57
61
  appearance: none;
58
62
  font: inherit;
59
63
  font-size: 0.88rem;
@@ -302,6 +302,7 @@ export class FileRequestBuilder extends BaseElement {
302
302
  if (!host) {
303
303
  return;
304
304
  }
305
+ const focusedSetting = this.shadowRoot.activeElement?.getAttribute("data-setting-id");
305
306
  host.innerHTML = `
306
307
  <section part="builder">
307
308
  <header part="header">
@@ -318,6 +319,8 @@ export class FileRequestBuilder extends BaseElement {
318
319
  </section>
319
320
  `;
320
321
  this.shadowRoot.querySelectorAll("[data-setting-id]").forEach(input => {
322
+ if (focusedSetting === input.dataset.settingId)
323
+ input.focus({ preventScroll: true });
321
324
  input.addEventListener("change", () => {
322
325
  const settingId = input.dataset.settingId;
323
326
  if (!settingId) {
@@ -0,0 +1,48 @@
1
+ import { BaseElement } from "../../core/index.js";
2
+ import { type FlowKind, type FlowNode, type NodePath } from "./model.js";
3
+ export type InspectorRenderer = (node: FlowNode, container: HTMLElement) => void | (() => void);
4
+ /** Host-owned document editing with accessible navigation and responsive inspection. */
5
+ export declare class FlowBuilder extends BaseElement {
6
+ static readonly tagName = "box-flow-builder";
7
+ private nodesValue;
8
+ private catalogValue;
9
+ private selectedValue;
10
+ private invalidValue;
11
+ private renderer?;
12
+ private cleanupInspector?;
13
+ private inspected;
14
+ private renderedRenderer?;
15
+ private observer?;
16
+ private narrow;
17
+ private insertion?;
18
+ private invoker?;
19
+ private spine;
20
+ private palette;
21
+ private picker;
22
+ private popup;
23
+ private aside;
24
+ private inspector;
25
+ private drawer;
26
+ private error;
27
+ get nodes(): FlowNode[];
28
+ set nodes(value: FlowNode[]);
29
+ get catalog(): readonly FlowKind[];
30
+ set catalog(value: readonly FlowKind[]);
31
+ get selected(): FlowNode | null;
32
+ set selected(node: FlowNode | null);
33
+ get renderInspector(): InspectorRenderer | undefined;
34
+ set renderInspector(renderer: InspectorRenderer | undefined);
35
+ connectedCallback(): void;
36
+ disconnectedCallback(): void;
37
+ private setNarrow;
38
+ protected renderTemplate(): void;
39
+ protected setupListeners(): void;
40
+ private closePicker;
41
+ private insert;
42
+ select(node: FlowNode | null): void;
43
+ /** Mark validation without opening a sheet over the error on a narrow screen. */
44
+ setValidation(message: string, path?: NodePath, document?: unknown): void;
45
+ refresh(): void;
46
+ protected update(): void;
47
+ private updateInspector;
48
+ }