@pi-archimedes/ui 2.9.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 (46) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +60 -0
  3. package/package.json +44 -0
  4. package/src/bash/index.ts +10 -0
  5. package/src/bash/renderer.test.ts +424 -0
  6. package/src/bash/renderer.ts +301 -0
  7. package/src/bash/tool.test.ts +26 -0
  8. package/src/bash/tool.ts +17 -0
  9. package/src/codemode/index.ts +20 -0
  10. package/src/codemode/loader.test.ts +139 -0
  11. package/src/codemode/loader.ts +115 -0
  12. package/src/codemode/renderer.test.ts +717 -0
  13. package/src/codemode/renderer.ts +399 -0
  14. package/src/codemode/tool.test.ts +149 -0
  15. package/src/codemode/tool.ts +96 -0
  16. package/src/config.test.ts +199 -0
  17. package/src/config.ts +129 -0
  18. package/src/editor/index.test.ts +820 -0
  19. package/src/editor/index.ts +380 -0
  20. package/src/editor/spin-quips.test.ts +106 -0
  21. package/src/editor/spin-quips.ts +44 -0
  22. package/src/editor/spin.test.ts +763 -0
  23. package/src/editor/spin.ts +493 -0
  24. package/src/index.test.ts +402 -0
  25. package/src/index.ts +235 -0
  26. package/src/migration.test.ts +256 -0
  27. package/src/migration.ts +94 -0
  28. package/src/settings.test.ts +122 -0
  29. package/src/settings.ts +135 -0
  30. package/src/startup/capture.ts +47 -0
  31. package/src/startup/index.test.ts +175 -0
  32. package/src/startup/index.ts +348 -0
  33. package/src/startup/logo.test.ts +260 -0
  34. package/src/startup/logo.ts +85 -0
  35. package/src/startup/sections.test.ts +333 -0
  36. package/src/startup/sections.ts +306 -0
  37. package/src/startup/version.test.ts +98 -0
  38. package/src/startup/version.ts +26 -0
  39. package/src/thinking/patch.test.ts +857 -0
  40. package/src/thinking/patch.ts +407 -0
  41. package/src/thinking/theme.test.ts +216 -0
  42. package/src/thinking/theme.ts +156 -0
  43. package/src/thinking/transform.test.ts +89 -0
  44. package/src/thinking/transform.ts +21 -0
  45. package/src/thinking/unindent.test.ts +74 -0
  46. package/src/thinking/unindent.ts +74 -0
@@ -0,0 +1,199 @@
1
+ import { describe, it, expect, vi, beforeEach } from "vitest";
2
+
3
+ vi.mock("@pi-archimedes/core/settings-io", () => ({
4
+ loadConfig: vi.fn(),
5
+ saveConfig: vi.fn(),
6
+ }));
7
+
8
+ import {
9
+ loadUIConfig,
10
+ saveUIConfig,
11
+ DEFAULT_UI_CONFIG,
12
+ ANIMATION_STYLES,
13
+ THINKING_STYLE_VALUES,
14
+ TOOL_STYLE_VALUES,
15
+ SPINNER_STYLES,
16
+ normalizeThinkingStyle,
17
+ normalizeToolStyle,
18
+ SPIN_SPEED_MULT,
19
+ } from "./config.js";
20
+ import { loadConfig, saveConfig } from "@pi-archimedes/core/settings-io";
21
+
22
+ describe("loadUIConfig", () => {
23
+ beforeEach(() => {
24
+ vi.clearAllMocks();
25
+ });
26
+
27
+ it("uses correct namespace archimedes.ui", () => {
28
+ vi.mocked(loadConfig).mockReturnValue(DEFAULT_UI_CONFIG);
29
+ loadUIConfig();
30
+ expect(loadConfig).toHaveBeenCalledWith("archimedes.ui", DEFAULT_UI_CONFIG);
31
+ });
32
+
33
+ it("returns default config when no settings exist", () => {
34
+ vi.mocked(loadConfig).mockReturnValue(DEFAULT_UI_CONFIG);
35
+ const result = loadUIConfig();
36
+ expect(result).toEqual({
37
+ bashToolStyling: true,
38
+ codemodeToolStyling: true,
39
+ mutedTheme: false,
40
+ autoCollapseThinking: false,
41
+ thinkingStyle: "Full",
42
+ toolStyle: "Minimal",
43
+ codeUnindent: true,
44
+ labelText: "Thinking...",
45
+ labelColor: "255,215,0",
46
+ animationStyle: "vertical-up",
47
+ editorSpinBorder: true,
48
+ editorSpinSpeed: "normal",
49
+ editorSpinLabel: "Working",
50
+ editorSpinStyle: "pendulum",
51
+ });
52
+ });
53
+
54
+ it("passes through merged config from settings-io", () => {
55
+ const merged = { ...DEFAULT_UI_CONFIG, bashToolStyling: false };
56
+ vi.mocked(loadConfig).mockReturnValue(merged);
57
+ const result = loadUIConfig();
58
+ expect(result).toEqual(merged);
59
+ });
60
+ });
61
+
62
+ describe("saveUIConfig", () => {
63
+ beforeEach(() => {
64
+ vi.clearAllMocks();
65
+ });
66
+
67
+ it("saves with correct namespace archimedes.ui", () => {
68
+ saveUIConfig(DEFAULT_UI_CONFIG);
69
+ expect(saveConfig).toHaveBeenCalledWith("archimedes.ui", DEFAULT_UI_CONFIG);
70
+ });
71
+
72
+ it("passes config through unchanged", () => {
73
+ const config = { ...DEFAULT_UI_CONFIG, bashToolStyling: false };
74
+ saveUIConfig(config);
75
+ expect(saveConfig).toHaveBeenCalledWith("archimedes.ui", config);
76
+ });
77
+ });
78
+
79
+ describe("DEFAULT_UI_CONFIG", () => {
80
+ it("has bashToolStyling and codemodeToolStyling enabled by default along with core defaults", () => {
81
+ expect(DEFAULT_UI_CONFIG).toEqual({
82
+ bashToolStyling: true,
83
+ codemodeToolStyling: true,
84
+ mutedTheme: false,
85
+ autoCollapseThinking: false,
86
+ thinkingStyle: "Full",
87
+ toolStyle: "Minimal",
88
+ codeUnindent: true,
89
+ labelText: "Thinking...",
90
+ labelColor: "255,215,0",
91
+ animationStyle: "vertical-up",
92
+ editorSpinBorder: true,
93
+ editorSpinSpeed: "normal",
94
+ editorSpinLabel: "Working",
95
+ editorSpinStyle: "pendulum",
96
+ });
97
+ });
98
+
99
+ it("exposes a speed→multiplier map (slow/normal/fast = 1.5/1/0.6)", () => {
100
+ expect(SPIN_SPEED_MULT).toEqual({ slow: 1.5, normal: 1, fast: 0.6 });
101
+ });
102
+ });
103
+
104
+ describe("ANIMATION_STYLES", () => {
105
+ it("contains all expected styles", () => {
106
+ expect(ANIMATION_STYLES).toEqual([
107
+ "diagonal",
108
+ "top-right",
109
+ "bottom-left",
110
+ "bottom-right",
111
+ "center-out",
112
+ "wave",
113
+ "horizontal",
114
+ "vertical",
115
+ "vertical-up",
116
+ ]);
117
+ });
118
+ });
119
+
120
+ describe("DEFAULT_UI_CONFIG styles", () => {
121
+ it("defaults thinkingStyle to Full and toolStyle to Minimal", () => {
122
+ expect(DEFAULT_UI_CONFIG.thinkingStyle).toBe("Full");
123
+ expect(DEFAULT_UI_CONFIG.toolStyle).toBe("Minimal");
124
+ });
125
+ });
126
+
127
+ describe("style value lists", () => {
128
+ it("exports the thinking and tool style values", () => {
129
+ expect(THINKING_STYLE_VALUES).toEqual(["Full", "Compact"]);
130
+ expect(TOOL_STYLE_VALUES).toEqual(["Native", "Minimal"]);
131
+ });
132
+ });
133
+
134
+ describe("SPINNER_STYLES", () => {
135
+ it("contains the 10 editorSpinStyle values in order", () => {
136
+ expect(SPINNER_STYLES).toEqual([
137
+ "typing",
138
+ "pulse",
139
+ "rain",
140
+ "cascade",
141
+ "columns",
142
+ "wave-rows",
143
+ "diagonal-swipe",
144
+ "sparkle",
145
+ "pendulum",
146
+ "marquee",
147
+ ]);
148
+ });
149
+ });
150
+
151
+ describe("normalizeThinkingStyle", () => {
152
+ it("normalizes current values as-is", () => {
153
+ expect(normalizeThinkingStyle("Full")).toBe("Full");
154
+ expect(normalizeThinkingStyle("Compact")).toBe("Compact");
155
+ });
156
+
157
+ it("maps legacy Off to Full", () => {
158
+ expect(normalizeThinkingStyle("Off")).toBe("Full");
159
+ });
160
+
161
+ it("maps legacy N-lines values to Compact", () => {
162
+ expect(normalizeThinkingStyle("1 line")).toBe("Compact");
163
+ expect(normalizeThinkingStyle("3 lines")).toBe("Compact");
164
+ expect(normalizeThinkingStyle("5 lines")).toBe("Compact");
165
+ });
166
+
167
+ it("normalizes invalid / unknown / non-string values to Full", () => {
168
+ expect(normalizeThinkingStyle(undefined)).toBe("Full");
169
+ expect(normalizeThinkingStyle(null)).toBe("Full");
170
+ expect(normalizeThinkingStyle("")).toBe("Full");
171
+ expect(normalizeThinkingStyle(123)).toBe("Full");
172
+ expect(normalizeThinkingStyle(true)).toBe("Full");
173
+ expect(normalizeThinkingStyle("off")).toBe("Full");
174
+ expect(normalizeThinkingStyle("2 lines")).toBe("Full");
175
+ });
176
+ });
177
+
178
+ describe("normalizeToolStyle", () => {
179
+ it("normalizes current values as-is", () => {
180
+ expect(normalizeToolStyle("Native")).toBe("Native");
181
+ expect(normalizeToolStyle("Minimal")).toBe("Minimal");
182
+ });
183
+
184
+ it("maps legacy Full (dropped auto-expand) to Minimal", () => {
185
+ expect(normalizeToolStyle("Full")).toBe("Minimal");
186
+ });
187
+
188
+ it("maps legacy Compact to Minimal (the upgrade keeps styling)", () => {
189
+ expect(normalizeToolStyle("Compact")).toBe("Minimal");
190
+ });
191
+
192
+ it("normalizes invalid / unknown / non-string values to Minimal", () => {
193
+ expect(normalizeToolStyle(undefined)).toBe("Minimal");
194
+ expect(normalizeToolStyle(null)).toBe("Minimal");
195
+ expect(normalizeToolStyle("")).toBe("Minimal");
196
+ expect(normalizeToolStyle(123)).toBe("Minimal");
197
+ expect(normalizeToolStyle(true)).toBe("Minimal");
198
+ });
199
+ });
package/src/config.ts ADDED
@@ -0,0 +1,129 @@
1
+ import { loadConfig, saveConfig } from "@pi-archimedes/core/settings-io";
2
+
3
+ export const ANIMATION_STYLES = [
4
+ "diagonal",
5
+ "top-right",
6
+ "bottom-left",
7
+ "bottom-right",
8
+ "center-out",
9
+ "wave",
10
+ "horizontal",
11
+ "vertical",
12
+ "vertical-up",
13
+ ] as const;
14
+ export type AnimationStyle = (typeof ANIMATION_STYLES)[number];
15
+
16
+ export type ThinkingStyle = "Full" | "Compact";
17
+ export const THINKING_STYLE_VALUES: readonly ThinkingStyle[] = [
18
+ "Full",
19
+ "Compact",
20
+ ] as const;
21
+
22
+ /**
23
+ * How tool rendering is handled. `Minimal` = the archimedes-styled tools
24
+ * (bash/codemode overrides, minimal collapsed view); `Native` = no archimedes
25
+ * styling at all — the overrides are not registered, so pi's native tool
26
+ * rendering stands (and no built-in-takeover startup notice).
27
+ */
28
+ export type ToolStyle = "Native" | "Minimal";
29
+ export const TOOL_STYLE_VALUES: readonly ToolStyle[] = [
30
+ "Native",
31
+ "Minimal",
32
+ ] as const;
33
+
34
+ export const SPINNER_STYLES: readonly SpinnerStyle[] = [
35
+ "typing",
36
+ "pulse",
37
+ "rain",
38
+ "cascade",
39
+ "columns",
40
+ "wave-rows",
41
+ "diagonal-swipe",
42
+ "sparkle",
43
+ "pendulum",
44
+ "marquee",
45
+ ];
46
+
47
+ export function normalizeThinkingStyle(value: unknown): ThinkingStyle {
48
+ if (value === "Full" || value === "Compact") return value;
49
+ if (value === "Off") return "Full";
50
+ if (value === "1 line" || value === "3 lines" || value === "5 lines") return "Compact";
51
+ return "Full";
52
+ }
53
+
54
+ /**
55
+ * Legacy mappings: `Full` (auto-expand, dropped) → `Minimal` (the user opted
56
+ * into archimedes styling, which is preserved); `Compact` (styled tools,
57
+ * native-collapsed presentation) → `Minimal` (styled tools — the upgrade
58
+ * must not silently strip styling from existing users).
59
+ */
60
+ export function normalizeToolStyle(value: unknown): ToolStyle {
61
+ if (value === "Native" || value === "Minimal") return value;
62
+ if (value === "Full") return "Minimal";
63
+ if (value === "Compact") return "Minimal";
64
+ return "Minimal";
65
+ }
66
+
67
+ export type SpinnerStyle =
68
+ | "typing"
69
+ | "pulse"
70
+ | "rain"
71
+ | "cascade"
72
+ | "columns"
73
+ | "wave-rows"
74
+ | "diagonal-swipe"
75
+ | "sparkle"
76
+ | "pendulum"
77
+ | "marquee";
78
+
79
+ export interface UIConfig {
80
+ bashToolStyling: boolean;
81
+ codemodeToolStyling: boolean;
82
+ mutedTheme: boolean;
83
+ autoCollapseThinking: boolean;
84
+ thinkingStyle: ThinkingStyle;
85
+ toolStyle: ToolStyle;
86
+ codeUnindent: boolean;
87
+ labelText: string;
88
+ labelColor: string;
89
+ animationStyle: AnimationStyle;
90
+ editorSpinBorder: boolean;
91
+ editorSpinSpeed: "slow" | "normal" | "fast";
92
+ editorSpinLabel: string;
93
+ editorSpinStyle: SpinnerStyle;
94
+ }
95
+
96
+ export type CoreConfig = UIConfig;
97
+
98
+ export const DEFAULT_UI_CONFIG: UIConfig = {
99
+ bashToolStyling: true,
100
+ codemodeToolStyling: true,
101
+ mutedTheme: false,
102
+ autoCollapseThinking: false,
103
+ thinkingStyle: "Full",
104
+ toolStyle: "Minimal",
105
+ codeUnindent: true,
106
+ labelText: "Thinking...",
107
+ labelColor: "255,215,0",
108
+ animationStyle: "vertical-up",
109
+ editorSpinBorder: true,
110
+ editorSpinSpeed: "normal",
111
+ editorSpinLabel: "Working",
112
+ editorSpinStyle: "pendulum",
113
+ };
114
+
115
+ export const SPIN_SPEED_MULT: Record<UIConfig["editorSpinSpeed"], number> = {
116
+ slow: 1.5,
117
+ normal: 1,
118
+ fast: 0.6,
119
+ };
120
+
121
+ const NAMESPACE = "archimedes.ui";
122
+
123
+ export function loadUIConfig(): UIConfig {
124
+ return loadConfig(NAMESPACE, DEFAULT_UI_CONFIG);
125
+ }
126
+
127
+ export function saveUIConfig(config: UIConfig): void {
128
+ saveConfig(NAMESPACE, config);
129
+ }