pilotswarm 0.0.1 → 0.4.1

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 (150) hide show
  1. package/README.md +37 -1
  2. package/mcp/README.md +484 -0
  3. package/mcp/dist/bin/pilotswarm-mcp.d.ts +3 -0
  4. package/mcp/dist/bin/pilotswarm-mcp.d.ts.map +1 -0
  5. package/mcp/dist/bin/pilotswarm-mcp.js +367 -0
  6. package/mcp/dist/bin/pilotswarm-mcp.js.map +1 -0
  7. package/mcp/dist/src/auth.d.ts +7 -0
  8. package/mcp/dist/src/auth.d.ts.map +1 -0
  9. package/mcp/dist/src/auth.js +99 -0
  10. package/mcp/dist/src/auth.js.map +1 -0
  11. package/mcp/dist/src/context.d.ts +48 -0
  12. package/mcp/dist/src/context.d.ts.map +1 -0
  13. package/mcp/dist/src/context.js +83 -0
  14. package/mcp/dist/src/context.js.map +1 -0
  15. package/mcp/dist/src/index.d.ts +4 -0
  16. package/mcp/dist/src/index.d.ts.map +1 -0
  17. package/mcp/dist/src/index.js +3 -0
  18. package/mcp/dist/src/index.js.map +1 -0
  19. package/mcp/dist/src/prompts/skills.d.ts +4 -0
  20. package/mcp/dist/src/prompts/skills.d.ts.map +1 -0
  21. package/mcp/dist/src/prompts/skills.js +11 -0
  22. package/mcp/dist/src/prompts/skills.js.map +1 -0
  23. package/mcp/dist/src/resources/agents.d.ts +4 -0
  24. package/mcp/dist/src/resources/agents.d.ts.map +1 -0
  25. package/mcp/dist/src/resources/agents.js +64 -0
  26. package/mcp/dist/src/resources/agents.js.map +1 -0
  27. package/mcp/dist/src/resources/facts.d.ts +4 -0
  28. package/mcp/dist/src/resources/facts.d.ts.map +1 -0
  29. package/mcp/dist/src/resources/facts.js +125 -0
  30. package/mcp/dist/src/resources/facts.js.map +1 -0
  31. package/mcp/dist/src/resources/models.d.ts +4 -0
  32. package/mcp/dist/src/resources/models.d.ts.map +1 -0
  33. package/mcp/dist/src/resources/models.js +43 -0
  34. package/mcp/dist/src/resources/models.js.map +1 -0
  35. package/mcp/dist/src/resources/sessions.d.ts +4 -0
  36. package/mcp/dist/src/resources/sessions.d.ts.map +1 -0
  37. package/mcp/dist/src/resources/sessions.js +190 -0
  38. package/mcp/dist/src/resources/sessions.js.map +1 -0
  39. package/mcp/dist/src/resources/subscriptions.d.ts +9 -0
  40. package/mcp/dist/src/resources/subscriptions.d.ts.map +1 -0
  41. package/mcp/dist/src/resources/subscriptions.js +157 -0
  42. package/mcp/dist/src/resources/subscriptions.js.map +1 -0
  43. package/mcp/dist/src/server.d.ts +4 -0
  44. package/mcp/dist/src/server.d.ts.map +1 -0
  45. package/mcp/dist/src/server.js +59 -0
  46. package/mcp/dist/src/server.js.map +1 -0
  47. package/mcp/dist/src/tools/agents.d.ts +4 -0
  48. package/mcp/dist/src/tools/agents.d.ts.map +1 -0
  49. package/mcp/dist/src/tools/agents.js +317 -0
  50. package/mcp/dist/src/tools/agents.js.map +1 -0
  51. package/mcp/dist/src/tools/facts.d.ts +4 -0
  52. package/mcp/dist/src/tools/facts.d.ts.map +1 -0
  53. package/mcp/dist/src/tools/facts.js +151 -0
  54. package/mcp/dist/src/tools/facts.js.map +1 -0
  55. package/mcp/dist/src/tools/models.d.ts +4 -0
  56. package/mcp/dist/src/tools/models.d.ts.map +1 -0
  57. package/mcp/dist/src/tools/models.js +256 -0
  58. package/mcp/dist/src/tools/models.js.map +1 -0
  59. package/mcp/dist/src/tools/sessions.d.ts +4 -0
  60. package/mcp/dist/src/tools/sessions.d.ts.map +1 -0
  61. package/mcp/dist/src/tools/sessions.js +606 -0
  62. package/mcp/dist/src/tools/sessions.js.map +1 -0
  63. package/mcp/dist/src/util/command.d.ts +52 -0
  64. package/mcp/dist/src/util/command.d.ts.map +1 -0
  65. package/mcp/dist/src/util/command.js +78 -0
  66. package/mcp/dist/src/util/command.js.map +1 -0
  67. package/package.json +82 -6
  68. package/tui/README.md +35 -0
  69. package/tui/bin/tui.js +30 -0
  70. package/tui/plugins/.mcp.json +7 -0
  71. package/tui/plugins/plugin.json +13 -0
  72. package/tui/plugins/session-policy.json +8 -0
  73. package/tui/src/app.js +850 -0
  74. package/tui/src/auth/cli.js +111 -0
  75. package/tui/src/auth/entra-auth.js +218 -0
  76. package/tui/src/bootstrap-env.js +176 -0
  77. package/tui/src/embedded-workers.js +79 -0
  78. package/tui/src/http-transport-host.js +106 -0
  79. package/tui/src/index.js +340 -0
  80. package/tui/src/node-sdk-transport.js +1794 -0
  81. package/tui/src/platform.js +984 -0
  82. package/tui/src/plugin-config.js +273 -0
  83. package/tui/src/portal.js +7 -0
  84. package/tui/src/version.js +7 -0
  85. package/tui/tui-splash-mobile.txt +7 -0
  86. package/tui/tui-splash.txt +11 -0
  87. package/ui/core/README.md +6 -0
  88. package/ui/core/src/commands.js +93 -0
  89. package/ui/core/src/context-usage.js +212 -0
  90. package/ui/core/src/controller.js +6201 -0
  91. package/ui/core/src/formatting.js +1036 -0
  92. package/ui/core/src/history.js +950 -0
  93. package/ui/core/src/index.js +13 -0
  94. package/ui/core/src/layout.js +332 -0
  95. package/ui/core/src/reducer.js +1952 -0
  96. package/ui/core/src/selectors.js +5419 -0
  97. package/ui/core/src/session-errors.js +14 -0
  98. package/ui/core/src/session-tree.js +151 -0
  99. package/ui/core/src/state.js +251 -0
  100. package/ui/core/src/store.js +23 -0
  101. package/ui/core/src/system-titles.js +24 -0
  102. package/ui/core/src/themes/catppuccin-mocha.js +56 -0
  103. package/ui/core/src/themes/cobalt2.js +56 -0
  104. package/ui/core/src/themes/dark-high-contrast.js +56 -0
  105. package/ui/core/src/themes/daylight.js +62 -0
  106. package/ui/core/src/themes/dracula.js +56 -0
  107. package/ui/core/src/themes/github-dark.js +56 -0
  108. package/ui/core/src/themes/github-light.js +59 -0
  109. package/ui/core/src/themes/gruvbox-dark.js +56 -0
  110. package/ui/core/src/themes/hacker-x-matrix.js +56 -0
  111. package/ui/core/src/themes/hacker-x-orion-prime.js +56 -0
  112. package/ui/core/src/themes/helpers.js +79 -0
  113. package/ui/core/src/themes/high-contrast-mono.js +59 -0
  114. package/ui/core/src/themes/index.js +52 -0
  115. package/ui/core/src/themes/light-high-contrast.js +62 -0
  116. package/ui/core/src/themes/noctis-obscuro.js +56 -0
  117. package/ui/core/src/themes/noctis.js +56 -0
  118. package/ui/core/src/themes/paper-ink.js +62 -0
  119. package/ui/core/src/themes/solarized-ops.js +59 -0
  120. package/ui/core/src/themes/terminal-green.js +59 -0
  121. package/ui/core/src/themes/tokyo-night.js +56 -0
  122. package/ui/react/README.md +5 -0
  123. package/ui/react/src/chat-status.js +39 -0
  124. package/ui/react/src/components.js +1989 -0
  125. package/ui/react/src/index.js +4 -0
  126. package/ui/react/src/platform.js +15 -0
  127. package/ui/react/src/use-controller-state.js +38 -0
  128. package/ui/react/src/web-app.js +4457 -0
  129. package/web/README.md +198 -0
  130. package/web/api/router.js +196 -0
  131. package/web/api/ws.js +152 -0
  132. package/web/auth/authz/engine.js +204 -0
  133. package/web/auth/config.js +115 -0
  134. package/web/auth/index.js +175 -0
  135. package/web/auth/normalize/entra.js +22 -0
  136. package/web/auth/providers/entra.js +76 -0
  137. package/web/auth/providers/none.js +24 -0
  138. package/web/auth.js +10 -0
  139. package/web/bin/serve.js +53 -0
  140. package/web/config.js +20 -0
  141. package/web/dist/app.js +469 -0
  142. package/web/dist/assets/index-DmGOcKR-.css +1 -0
  143. package/web/dist/assets/index-xJ8IzIZY.js +24 -0
  144. package/web/dist/assets/msal-CytV9RFv.js +7 -0
  145. package/web/dist/assets/pilotswarm-D9pEmenA.js +90 -0
  146. package/web/dist/assets/react-CEPDSRB6.js +1 -0
  147. package/web/dist/index.html +16 -0
  148. package/web/runtime.js +455 -0
  149. package/web/server.js +276 -0
  150. package/index.js +0 -1
@@ -0,0 +1,56 @@
1
+ import { createTheme } from "./helpers.js";
2
+
3
+ const noctisObscuroTheme = createTheme({
4
+ id: "noctis-obscuro",
5
+ label: "Noctis Obscuro",
6
+ description: "Deep obsidian palette with cool blue accents and restrained contrast for long sessions.",
7
+ page: {
8
+ background: "#0b1117",
9
+ foreground: "#c7d1da",
10
+ overlayBackground: "#0b1117",
11
+ overlayForeground: "#c7d1da",
12
+ hintColor: "#7cc6ff",
13
+ modalBackdrop: "rgba(5, 9, 13, 0.74)",
14
+ modalBackground: "#121a23",
15
+ modalBorder: "#28455b",
16
+ modalForeground: "#d7e2eb",
17
+ modalMuted: "#8f9dab",
18
+ modalSelectedBackground: "#173041",
19
+ modalSelectedBorder: "#7cc6ff",
20
+ modalSelectedForeground: "#eef6ff",
21
+ },
22
+ terminal: {
23
+ background: "#060b10",
24
+ foreground: "#c7d1da",
25
+ cursor: "#9fd4ff",
26
+ cursorAccent: "#060b10",
27
+ selectionBackground: "rgba(72, 125, 170, 0.30)",
28
+ black: "#2a3138",
29
+ red: "#ef6b73",
30
+ green: "#7ddc8f",
31
+ yellow: "#d9b36c",
32
+ blue: "#68aee8",
33
+ magenta: "#b28cff",
34
+ cyan: "#58c9d4",
35
+ white: "#c7d1da",
36
+ brightBlack: "#6e7a86",
37
+ brightRed: "#f28a92",
38
+ brightGreen: "#97e5a6",
39
+ brightYellow: "#e4c28b",
40
+ brightBlue: "#86c1f1",
41
+ brightMagenta: "#c6a7ff",
42
+ brightCyan: "#7ad7df",
43
+ brightWhite: "#e5edf5",
44
+ },
45
+ tui: {
46
+ surface: "#060b10",
47
+ activeHighlightBackground: "#123142",
48
+ activeHighlightForeground: "#eef6ff",
49
+ selectionBackground: "#4a7baa",
50
+ selectionForeground: "#0b1117",
51
+ promptCursorBackground: "#9fd4ff",
52
+ promptCursorForeground: "#060b10",
53
+ },
54
+ });
55
+
56
+ export default noctisObscuroTheme;
@@ -0,0 +1,56 @@
1
+ import { createTheme } from "./helpers.js";
2
+
3
+ const noctisTheme = createTheme({
4
+ id: "noctis",
5
+ label: "Noctis",
6
+ description: "Dark cyan-tinted palette with warm and cold medium contrast accents.",
7
+ page: {
8
+ background: "#052529",
9
+ foreground: "#b2cacd",
10
+ overlayBackground: "#052529",
11
+ overlayForeground: "#b2cacd",
12
+ hintColor: "#40d4e7",
13
+ modalBackdrop: "rgba(3, 25, 27, 0.72)",
14
+ modalBackground: "#073940",
15
+ modalBorder: "#0e6671",
16
+ modalForeground: "#b2cacd",
17
+ modalMuted: "#87a7ab",
18
+ modalSelectedBackground: "#0b515b",
19
+ modalSelectedBorder: "#40d4e7",
20
+ modalSelectedForeground: "#ebfdff",
21
+ },
22
+ terminal: {
23
+ background: "#03191b",
24
+ foreground: "#b2cacd",
25
+ cursor: "#85efff",
26
+ cursorAccent: "#03191b",
27
+ selectionBackground: "rgba(22, 159, 177, 0.27)",
28
+ black: "#324a4d",
29
+ red: "#e66533",
30
+ green: "#49e9a6",
31
+ yellow: "#e4b781",
32
+ blue: "#49ace9",
33
+ magenta: "#df769b",
34
+ cyan: "#49d6e9",
35
+ white: "#b2cacd",
36
+ brightBlack: "#5e878c",
37
+ brightRed: "#e97749",
38
+ brightGreen: "#60ebb1",
39
+ brightYellow: "#e69533",
40
+ brightBlue: "#60b6eb",
41
+ brightMagenta: "#e798b3",
42
+ brightCyan: "#60dbeb",
43
+ brightWhite: "#c1d4d7",
44
+ },
45
+ tui: {
46
+ surface: "#03191b",
47
+ activeHighlightBackground: "#083d44",
48
+ activeHighlightForeground: "#ebfdff",
49
+ selectionBackground: "#169fb1",
50
+ selectionForeground: "#052529",
51
+ promptCursorBackground: "#85efff",
52
+ promptCursorForeground: "#03191b",
53
+ },
54
+ });
55
+
56
+ export default noctisTheme;
@@ -0,0 +1,62 @@
1
+ import { createTheme } from "./helpers.js";
2
+
3
+ // Warm light theme: paper background, true-ink text, letterpress accents.
4
+ // Warmth comes from the background — the text colors stay deep so agent
5
+ // output never washes out.
6
+ const paperInkTheme = createTheme({
7
+ id: "paper-ink",
8
+ label: "Paper Ink",
9
+ description: "Warm paper background with true-ink text and letterpress accents — warmth without washed-out output.",
10
+ page: {
11
+ background: "#faf6ef",
12
+ foreground: "#1c1917",
13
+ overlayBackground: "#fffdf8",
14
+ overlayForeground: "#1c1917",
15
+ hintColor: "#57534e",
16
+ modalBackdrop: "rgba(28, 25, 23, 0.35)",
17
+ modalBackground: "#fffdf8",
18
+ modalBorder: "#d6cfc2",
19
+ modalForeground: "#1c1917",
20
+ modalMuted: "#57534e",
21
+ modalSelectedBackground: "#f1e4c7",
22
+ modalSelectedBorder: "#854d0e",
23
+ modalSelectedForeground: "#713f12",
24
+ },
25
+ terminal: {
26
+ background: "#faf6ef",
27
+ foreground: "#1c1917",
28
+ cursor: "#9f1239",
29
+ cursorAccent: "#fffdf8",
30
+ selectionBackground: "#f1e4c7",
31
+ black: "#1c1917",
32
+ red: "#9f1239",
33
+ green: "#3f6212",
34
+ yellow: "#854d0e",
35
+ blue: "#1e40af",
36
+ magenta: "#86198f",
37
+ cyan: "#0f766e",
38
+ white: "#57534e",
39
+ brightBlack: "#44403c",
40
+ brightRed: "#881337",
41
+ brightGreen: "#365314",
42
+ brightYellow: "#713f12",
43
+ brightBlue: "#1e3a8a",
44
+ brightMagenta: "#701a75",
45
+ brightCyan: "#115e59",
46
+ brightWhite: "#1c1917",
47
+ },
48
+ tui: {
49
+ surface: "#fffdf8",
50
+ border: "#d6cfc2",
51
+ userChat: "#1e40af",
52
+ userChatLabel: "#1e3a8a",
53
+ activeHighlightBackground: "#f1e4c7",
54
+ activeHighlightForeground: "#713f12",
55
+ selectionBackground: "#854d0e",
56
+ selectionForeground: "#fffdf8",
57
+ promptCursorBackground: "#9f1239",
58
+ promptCursorForeground: "#fffdf8",
59
+ },
60
+ });
61
+
62
+ export default paperInkTheme;
@@ -0,0 +1,59 @@
1
+ import { createTheme } from "./helpers.js";
2
+
3
+ const solarizedOpsTheme = createTheme({
4
+ id: "solarized-ops",
5
+ label: "Solarized Ops",
6
+ description: "Solarized-inspired operations palette with deep blue-green surfaces and balanced long-session contrast.",
7
+ page: {
8
+ background: "#002b36",
9
+ foreground: "#d7e2d0",
10
+ overlayBackground: "#073642",
11
+ overlayForeground: "#e5efe4",
12
+ hintColor: "#93a1a1",
13
+ modalBackdrop: "rgba(0, 20, 25, 0.74)",
14
+ modalBackground: "#073642",
15
+ modalBorder: "#2f5d67",
16
+ modalForeground: "#e5efe4",
17
+ modalMuted: "#93a1a1",
18
+ modalSelectedBackground: "#164b56",
19
+ modalSelectedBorder: "#2aa198",
20
+ modalSelectedForeground: "#fdf6e3",
21
+ },
22
+ terminal: {
23
+ background: "#002b36",
24
+ foreground: "#d7e2d0",
25
+ cursor: "#2aa198",
26
+ cursorAccent: "#002b36",
27
+ selectionBackground: "#164b56",
28
+ black: "#073642",
29
+ red: "#dc322f",
30
+ green: "#859900",
31
+ yellow: "#b58900",
32
+ blue: "#268bd2",
33
+ magenta: "#6c71c4",
34
+ cyan: "#2aa198",
35
+ white: "#eee8d5",
36
+ brightBlack: "#586e75",
37
+ brightRed: "#cb4b16",
38
+ brightGreen: "#93a100",
39
+ brightYellow: "#c79b00",
40
+ brightBlue: "#3aa0e6",
41
+ brightMagenta: "#7b80d6",
42
+ brightCyan: "#36b5ac",
43
+ brightWhite: "#fdf6e3",
44
+ },
45
+ tui: {
46
+ surface: "#073642",
47
+ border: "#2f5d67",
48
+ userChat: "#b58900",
49
+ userChatLabel: "#f0d47a",
50
+ activeHighlightBackground: "#164b56",
51
+ activeHighlightForeground: "#fdf6e3",
52
+ selectionBackground: "#268bd2",
53
+ selectionForeground: "#002b36",
54
+ promptCursorBackground: "#2aa198",
55
+ promptCursorForeground: "#002b36",
56
+ },
57
+ });
58
+
59
+ export default solarizedOpsTheme;
@@ -0,0 +1,59 @@
1
+ import { createTheme } from "./helpers.js";
2
+
3
+ const terminalGreenTheme = createTheme({
4
+ id: "terminal-green",
5
+ label: "Terminal Green",
6
+ description: "Classic black terminal with phosphor green text, amber warnings, and crisp operator contrast.",
7
+ page: {
8
+ background: "#020503",
9
+ foreground: "#c9f7d3",
10
+ overlayBackground: "#020503",
11
+ overlayForeground: "#d9ffe0",
12
+ hintColor: "#7ee787",
13
+ modalBackdrop: "rgba(0, 6, 2, 0.82)",
14
+ modalBackground: "#061109",
15
+ modalBorder: "#245c33",
16
+ modalForeground: "#d9ffe0",
17
+ modalMuted: "#83a98c",
18
+ modalSelectedBackground: "#0b2a14",
19
+ modalSelectedBorder: "#63e27a",
20
+ modalSelectedForeground: "#ecfff0",
21
+ },
22
+ terminal: {
23
+ background: "#020503",
24
+ foreground: "#c9f7d3",
25
+ cursor: "#8cff9f",
26
+ cursorAccent: "#020503",
27
+ selectionBackground: "#174923",
28
+ black: "#061109",
29
+ red: "#ff5f5f",
30
+ green: "#63e27a",
31
+ yellow: "#d6b95f",
32
+ blue: "#5aa7ff",
33
+ magenta: "#c586ff",
34
+ cyan: "#5ce1d8",
35
+ white: "#c9f7d3",
36
+ brightBlack: "#5b7a61",
37
+ brightRed: "#ff7f7f",
38
+ brightGreen: "#8cff9f",
39
+ brightYellow: "#f0d982",
40
+ brightBlue: "#82bdff",
41
+ brightMagenta: "#d9a8ff",
42
+ brightCyan: "#89f4ee",
43
+ brightWhite: "#effff1",
44
+ },
45
+ tui: {
46
+ surface: "#020503",
47
+ border: "#245c33",
48
+ userChat: "#f0d982",
49
+ userChatLabel: "#fff0a8",
50
+ activeHighlightBackground: "#0b2a14",
51
+ activeHighlightForeground: "#ecfff0",
52
+ selectionBackground: "#63e27a",
53
+ selectionForeground: "#020503",
54
+ promptCursorBackground: "#8cff9f",
55
+ promptCursorForeground: "#020503",
56
+ },
57
+ });
58
+
59
+ export default terminalGreenTheme;
@@ -0,0 +1,56 @@
1
+ import { createTheme } from "./helpers.js";
2
+
3
+ const tokyoNightTheme = createTheme({
4
+ id: "tokyo-night",
5
+ label: "Tokyo Night",
6
+ description: "Deep blue-gray canvas with vivid neon accents inspired by Tokyo city lights.",
7
+ page: {
8
+ background: "#1a1b26",
9
+ foreground: "#a9b1d6",
10
+ overlayBackground: "#1a1b26",
11
+ overlayForeground: "#a9b1d6",
12
+ hintColor: "#737aa2",
13
+ modalBackdrop: "rgba(26, 27, 38, 0.74)",
14
+ modalBackground: "#24283b",
15
+ modalBorder: "#414868",
16
+ modalForeground: "#a9b1d6",
17
+ modalMuted: "#737aa2",
18
+ modalSelectedBackground: "#33467c",
19
+ modalSelectedBorder: "#7aa2f7",
20
+ modalSelectedForeground: "#c0caf5",
21
+ },
22
+ terminal: {
23
+ background: "#1a1b26",
24
+ foreground: "#a9b1d6",
25
+ cursor: "#c0caf5",
26
+ cursorAccent: "#1a1b26",
27
+ selectionBackground: "rgba(122, 162, 247, 0.22)",
28
+ black: "#414868",
29
+ red: "#f7768e",
30
+ green: "#9ece6a",
31
+ yellow: "#e0af68",
32
+ blue: "#7aa2f7",
33
+ magenta: "#bb9af7",
34
+ cyan: "#7dcfff",
35
+ white: "#a9b1d6",
36
+ brightBlack: "#565f89",
37
+ brightRed: "#f7768e",
38
+ brightGreen: "#9ece6a",
39
+ brightYellow: "#e0af68",
40
+ brightBlue: "#7aa2f7",
41
+ brightMagenta: "#bb9af7",
42
+ brightCyan: "#7dcfff",
43
+ brightWhite: "#c0caf5",
44
+ },
45
+ tui: {
46
+ surface: "#1a1b26",
47
+ activeHighlightBackground: "#33467c",
48
+ activeHighlightForeground: "#c0caf5",
49
+ selectionBackground: "#7aa2f7",
50
+ selectionForeground: "#1a1b26",
51
+ promptCursorBackground: "#c0caf5",
52
+ promptCursorForeground: "#1a1b26",
53
+ },
54
+ });
55
+
56
+ export default tokyoNightTheme;
@@ -0,0 +1,5 @@
1
+ # pilotswarm/ui-react (internal)
2
+
3
+ Shared React views and host adapters for PilotSwarm interfaces.
4
+
5
+ This package is used by both the TUI and web portal host layers.
@@ -0,0 +1,39 @@
1
+ import React from "react";
2
+
3
+ const DOT_FRAMES = [".\u00a0\u00a0", "..\u00a0", "..."];
4
+
5
+ export function useAnimatedDots(active, intervalMs = 360) {
6
+ const [frameIndex, setFrameIndex] = React.useState(0);
7
+
8
+ React.useEffect(() => {
9
+ if (!active) {
10
+ setFrameIndex(0);
11
+ return undefined;
12
+ }
13
+
14
+ const timer = setInterval(() => {
15
+ setFrameIndex((current) => (current + 1) % DOT_FRAMES.length);
16
+ }, intervalMs);
17
+
18
+ return () => clearInterval(timer);
19
+ }, [active, intervalMs]);
20
+
21
+ return active ? DOT_FRAMES[frameIndex] : "";
22
+ }
23
+
24
+ export function appendAnimatedDotsToRuns(runs, dots = "") {
25
+ if (!Array.isArray(runs) || runs.length === 0) return null;
26
+ if (!dots) return runs;
27
+
28
+ const lastRun = runs[runs.length - 1] || {};
29
+ return [
30
+ ...runs,
31
+ {
32
+ text: dots,
33
+ color: lastRun.color || "gray",
34
+ bold: Boolean(lastRun.bold),
35
+ underline: Boolean(lastRun.underline),
36
+ backgroundColor: lastRun.backgroundColor,
37
+ },
38
+ ];
39
+ }