doe-sdk 0.1.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 (127) hide show
  1. package/README.md +509 -0
  2. package/dist/cli/index.d.ts +17 -0
  3. package/dist/cli/index.d.ts.map +1 -0
  4. package/dist/cli/index.js +118 -0
  5. package/dist/cli/index.js.map +1 -0
  6. package/dist/cli/init.d.ts +25 -0
  7. package/dist/cli/init.d.ts.map +1 -0
  8. package/dist/cli/init.js +100 -0
  9. package/dist/cli/init.js.map +1 -0
  10. package/dist/cli/login.d.ts +52 -0
  11. package/dist/cli/login.d.ts.map +1 -0
  12. package/dist/cli/login.js +571 -0
  13. package/dist/cli/login.js.map +1 -0
  14. package/dist/cli/publish.d.ts +27 -0
  15. package/dist/cli/publish.d.ts.map +1 -0
  16. package/dist/cli/publish.js +531 -0
  17. package/dist/cli/publish.js.map +1 -0
  18. package/dist/cli/scaffold.d.ts +18 -0
  19. package/dist/cli/scaffold.d.ts.map +1 -0
  20. package/dist/cli/scaffold.js +252 -0
  21. package/dist/cli/scaffold.js.map +1 -0
  22. package/dist/cli/ui.d.ts +57 -0
  23. package/dist/cli/ui.d.ts.map +1 -0
  24. package/dist/cli/ui.js +339 -0
  25. package/dist/cli/ui.js.map +1 -0
  26. package/dist/cli/validate.d.ts +28 -0
  27. package/dist/cli/validate.d.ts.map +1 -0
  28. package/dist/cli/validate.js +1270 -0
  29. package/dist/cli/validate.js.map +1 -0
  30. package/dist/compat/legacy-adapter.d.ts +198 -0
  31. package/dist/compat/legacy-adapter.d.ts.map +1 -0
  32. package/dist/compat/legacy-adapter.js +318 -0
  33. package/dist/compat/legacy-adapter.js.map +1 -0
  34. package/dist/index.d.ts +14 -0
  35. package/dist/index.d.ts.map +1 -0
  36. package/dist/index.js +16 -0
  37. package/dist/index.js.map +1 -0
  38. package/dist/runtime/client.d.ts +370 -0
  39. package/dist/runtime/client.d.ts.map +1 -0
  40. package/dist/runtime/client.js +470 -0
  41. package/dist/runtime/client.js.map +1 -0
  42. package/dist/runtime/index.d.ts +8 -0
  43. package/dist/runtime/index.d.ts.map +1 -0
  44. package/dist/runtime/index.js +7 -0
  45. package/dist/runtime/index.js.map +1 -0
  46. package/dist/types/api.d.ts +564 -0
  47. package/dist/types/api.d.ts.map +1 -0
  48. package/dist/types/api.js +11 -0
  49. package/dist/types/api.js.map +1 -0
  50. package/dist/types/index.d.ts +12 -0
  51. package/dist/types/index.d.ts.map +1 -0
  52. package/dist/types/index.js +10 -0
  53. package/dist/types/index.js.map +1 -0
  54. package/dist/types/manifest.d.ts +412 -0
  55. package/dist/types/manifest.d.ts.map +1 -0
  56. package/dist/types/manifest.js +42 -0
  57. package/dist/types/manifest.js.map +1 -0
  58. package/dist/types/marketplace-categories.d.ts +9 -0
  59. package/dist/types/marketplace-categories.d.ts.map +1 -0
  60. package/dist/types/marketplace-categories.js +16 -0
  61. package/dist/types/marketplace-categories.js.map +1 -0
  62. package/dist/types/permissions.d.ts +86 -0
  63. package/dist/types/permissions.d.ts.map +1 -0
  64. package/dist/types/permissions.js +295 -0
  65. package/dist/types/permissions.js.map +1 -0
  66. package/dist/types/theme/index.d.ts +7 -0
  67. package/dist/types/theme/index.d.ts.map +1 -0
  68. package/dist/types/theme/index.js +7 -0
  69. package/dist/types/theme/index.js.map +1 -0
  70. package/dist/types/theme/schema.d.ts +1205 -0
  71. package/dist/types/theme/schema.d.ts.map +1 -0
  72. package/dist/types/theme/schema.js +325 -0
  73. package/dist/types/theme/schema.js.map +1 -0
  74. package/dist/types/theme/types.d.ts +648 -0
  75. package/dist/types/theme/types.d.ts.map +1 -0
  76. package/dist/types/theme/types.js +8 -0
  77. package/dist/types/theme/types.js.map +1 -0
  78. package/package.json +75 -0
  79. package/templates/extension/README.md +254 -0
  80. package/templates/extension/icon.png +0 -0
  81. package/templates/extension/index.html +42 -0
  82. package/templates/extension/manifest.json +57 -0
  83. package/templates/extension/package.json +24 -0
  84. package/templates/extension/src/App.tsx +252 -0
  85. package/templates/extension/src/components/OnboardingComplete.tsx +190 -0
  86. package/templates/extension/src/components/OnboardingProgress.tsx +82 -0
  87. package/templates/extension/src/components/OnboardingWelcome.tsx +166 -0
  88. package/templates/extension/src/components/StepContainer.tsx +217 -0
  89. package/templates/extension/src/components/playground/CanvasTab.tsx +24 -0
  90. package/templates/extension/src/components/playground/ConfigTab.tsx +24 -0
  91. package/templates/extension/src/components/playground/EventsTab.tsx +24 -0
  92. package/templates/extension/src/components/playground/InfoTab.tsx +89 -0
  93. package/templates/extension/src/components/playground/NetworkTab.tsx +24 -0
  94. package/templates/extension/src/components/playground/PlaygroundContainer.tsx +184 -0
  95. package/templates/extension/src/components/playground/ResultDisplay.tsx +30 -0
  96. package/templates/extension/src/components/playground/StorageTab.tsx +24 -0
  97. package/templates/extension/src/components/playground/UITab.tsx +24 -0
  98. package/templates/extension/src/components/shared/CanvasControls.tsx +130 -0
  99. package/templates/extension/src/components/shared/ConfigControls.tsx +154 -0
  100. package/templates/extension/src/components/shared/EventsControls.tsx +232 -0
  101. package/templates/extension/src/components/shared/InfoControls.tsx +281 -0
  102. package/templates/extension/src/components/shared/NetworkControls.tsx +328 -0
  103. package/templates/extension/src/components/shared/StorageControls.tsx +203 -0
  104. package/templates/extension/src/components/shared/UIControls.tsx +199 -0
  105. package/templates/extension/src/components/shared/index.ts +15 -0
  106. package/templates/extension/src/components/steps/CanvasStep.tsx +67 -0
  107. package/templates/extension/src/components/steps/ClipboardStep.tsx +167 -0
  108. package/templates/extension/src/components/steps/ConfigStep.tsx +63 -0
  109. package/templates/extension/src/components/steps/EventsStep.tsx +69 -0
  110. package/templates/extension/src/components/steps/InfoStep.tsx +70 -0
  111. package/templates/extension/src/components/steps/NetworkStep.tsx +70 -0
  112. package/templates/extension/src/components/steps/StorageStep.tsx +61 -0
  113. package/templates/extension/src/components/steps/UIStep.tsx +70 -0
  114. package/templates/extension/src/hooks/useDoe.ts +93 -0
  115. package/templates/extension/src/hooks/useOnboarding.ts +264 -0
  116. package/templates/extension/src/hooks/usePointerHandlers.ts +105 -0
  117. package/templates/extension/src/main.tsx +18 -0
  118. package/templates/extension/src/styles.ts +265 -0
  119. package/templates/extension/tsconfig.json +28 -0
  120. package/templates/extension/vite.config.ts +32 -0
  121. package/templates/theme/README.md +132 -0
  122. package/templates/theme/manifest.json +19 -0
  123. package/templates/theme/package.json +16 -0
  124. package/templates/theme/styles/.gitkeep +2 -0
  125. package/templates/theme/themes/theme.json +32 -0
  126. package/templates/theme/vite-plugin-doe-theme.ts +53 -0
  127. package/templates/theme/vite.config.ts +10 -0
@@ -0,0 +1,217 @@
1
+ /**
2
+ * StepContainer - Wrapper for onboarding step content
3
+ *
4
+ * Provides consistent layout, navigation, and code toggle functionality.
5
+ */
6
+ import React, { useState, useRef, type ReactNode } from "react";
7
+ import {
8
+ useInteractivePointerDown,
9
+ usePreventCanvasZoom,
10
+ } from "../hooks/usePointerHandlers";
11
+ import {
12
+ spacing,
13
+ colors,
14
+ typography,
15
+ buttonStyles,
16
+ cardStyles,
17
+ codeStyles,
18
+ flexBetween,
19
+ animationStyles,
20
+ } from "../styles";
21
+
22
+ export interface StepContainerProps {
23
+ /** Step title */
24
+ title: string;
25
+ /** Step description */
26
+ description: string;
27
+ /** Step icon (emoji) */
28
+ icon: string;
29
+ /** Current step number (1-based for display) */
30
+ stepNumber: number;
31
+ /** Total number of steps */
32
+ totalSteps: number;
33
+ /** Interactive demo content */
34
+ children: ReactNode;
35
+ /** Code example to show */
36
+ codeExample?: string;
37
+ /** Result/output to display */
38
+ result?: ReactNode;
39
+ /** Navigate to previous step */
40
+ onPrev: () => void;
41
+ /** Navigate to next step (or complete) */
42
+ onNext: () => void;
43
+ /** Whether this is the last step */
44
+ isLastStep?: boolean;
45
+ }
46
+
47
+ export function StepContainer({
48
+ title,
49
+ description,
50
+ icon,
51
+ stepNumber,
52
+ totalSteps,
53
+ children,
54
+ codeExample,
55
+ result,
56
+ onPrev,
57
+ onNext,
58
+ isLastStep = false,
59
+ }: StepContainerProps): React.ReactElement {
60
+ const [showCode, setShowCode] = useState(false);
61
+ const handlePointerDown = useInteractivePointerDown();
62
+ const scrollRef = useRef<HTMLDivElement>(null);
63
+ usePreventCanvasZoom(scrollRef);
64
+
65
+ return (
66
+ <div
67
+ ref={scrollRef}
68
+ style={{
69
+ display: "flex",
70
+ flexDirection: "column",
71
+ height: "100%",
72
+ overflow: "auto",
73
+ animation: "slideInRight 0.3s ease-out",
74
+ }}
75
+ >
76
+ <style>{animationStyles}</style>
77
+
78
+ {/* Header */}
79
+ <div style={{ marginBottom: spacing.lg }}>
80
+ <div
81
+ style={{
82
+ display: "flex",
83
+ alignItems: "center",
84
+ gap: spacing.sm,
85
+ marginBottom: spacing.xs,
86
+ }}
87
+ >
88
+ <span style={{ fontSize: typography.sizes.xl }}>{icon}</span>
89
+ <h2
90
+ style={{
91
+ fontSize: typography.sizes.xl,
92
+ fontWeight: typography.weights.semibold,
93
+ color: colors.text,
94
+ margin: 0,
95
+ }}
96
+ >
97
+ {title}
98
+ </h2>
99
+ </div>
100
+ <p
101
+ style={{
102
+ fontSize: typography.sizes.base,
103
+ color: colors.textSecondary,
104
+ margin: 0,
105
+ }}
106
+ >
107
+ {description}
108
+ </p>
109
+ </div>
110
+
111
+ {/* Demo Area */}
112
+ <div style={{ ...cardStyles.base, marginBottom: spacing.md, flex: 1 }}>
113
+ {children}
114
+
115
+ {/* Result Display */}
116
+ {result && (
117
+ <div
118
+ style={{
119
+ marginTop: spacing.md,
120
+ padding: spacing.sm,
121
+ background: colors.backgroundHover,
122
+ borderRadius: 6,
123
+ fontSize: typography.sizes.sm,
124
+ fontFamily: "monospace",
125
+ color: colors.textSecondary,
126
+ wordBreak: "break-word",
127
+ }}
128
+ >
129
+ {result}
130
+ </div>
131
+ )}
132
+ </div>
133
+
134
+ {/* Code Toggle */}
135
+ {codeExample && (
136
+ <div style={{ marginBottom: spacing.md }}>
137
+ <button
138
+ onClick={() => setShowCode(!showCode)}
139
+ onPointerDown={handlePointerDown}
140
+ style={{
141
+ ...buttonStyles.link,
142
+ display: "flex",
143
+ alignItems: "center",
144
+ gap: spacing.xs,
145
+ padding: 0,
146
+ marginBottom: spacing.sm,
147
+ }}
148
+ >
149
+ <span
150
+ style={{
151
+ transform: showCode ? "rotate(90deg)" : "rotate(0deg)",
152
+ transition: "transform 0.15s ease",
153
+ display: "inline-block",
154
+ }}
155
+ >
156
+
157
+ </span>
158
+ {showCode ? "Hide Code" : "Show Code"}
159
+ </button>
160
+
161
+ {showCode && (
162
+ <div
163
+ style={{
164
+ ...codeStyles.container,
165
+ animation: "slideInUp 0.2s ease-out",
166
+ }}
167
+ >
168
+ <pre style={{ margin: 0, whiteSpace: "pre-wrap" }}>
169
+ <code style={{ color: codeStyles.plain.color }}>
170
+ {codeExample}
171
+ </code>
172
+ </pre>
173
+ </div>
174
+ )}
175
+ </div>
176
+ )}
177
+
178
+ {/* Navigation */}
179
+ <div
180
+ style={{
181
+ ...flexBetween,
182
+ paddingTop: spacing.md,
183
+ borderTop: `1px solid ${colors.border}`,
184
+ }}
185
+ >
186
+ <button
187
+ onClick={onPrev}
188
+ onPointerDown={handlePointerDown}
189
+ style={{
190
+ ...buttonStyles.secondary,
191
+ opacity: stepNumber === 1 ? 0.5 : 1,
192
+ }}
193
+ disabled={stepNumber === 1}
194
+ >
195
+ ← Back
196
+ </button>
197
+
198
+ <span
199
+ style={{
200
+ fontSize: typography.sizes.sm,
201
+ color: colors.textMuted,
202
+ }}
203
+ >
204
+ {stepNumber} / {totalSteps}
205
+ </span>
206
+
207
+ <button
208
+ onClick={onNext}
209
+ onPointerDown={handlePointerDown}
210
+ style={buttonStyles.primary}
211
+ >
212
+ {isLastStep ? "Complete" : "Next →"}
213
+ </button>
214
+ </div>
215
+ </div>
216
+ );
217
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * CanvasTab - Playground tab for Canvas API exploration
3
+ *
4
+ * Uses shared CanvasControls component for DRY code.
5
+ */
6
+ import React, { useState } from "react";
7
+ import type { DOEExtensionAPI } from "doe-sdk/runtime";
8
+ import { CanvasControls } from "../shared";
9
+ import { ResultDisplay } from "./ResultDisplay";
10
+
11
+ export interface CanvasTabProps {
12
+ api: DOEExtensionAPI;
13
+ }
14
+
15
+ export function CanvasTab({ api }: CanvasTabProps): React.ReactElement {
16
+ const [result, setResult] = useState<string>("");
17
+
18
+ return (
19
+ <div>
20
+ <CanvasControls api={api} onResult={setResult} />
21
+ <ResultDisplay result={result} />
22
+ </div>
23
+ );
24
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * ConfigTab - Playground tab for Config API exploration
3
+ *
4
+ * Uses shared ConfigControls component for DRY code.
5
+ */
6
+ import React, { useState } from "react";
7
+ import type { DOEExtensionAPI } from "doe-sdk/runtime";
8
+ import { ConfigControls } from "../shared";
9
+ import { ResultDisplay } from "./ResultDisplay";
10
+
11
+ export interface ConfigTabProps {
12
+ api: DOEExtensionAPI;
13
+ }
14
+
15
+ export function ConfigTab({ api }: ConfigTabProps): React.ReactElement {
16
+ const [result, setResult] = useState<string>("");
17
+
18
+ return (
19
+ <div>
20
+ <ConfigControls api={api} onResult={setResult} />
21
+ <ResultDisplay result={result} />
22
+ </div>
23
+ );
24
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * EventsTab - Playground tab for Events API exploration
3
+ *
4
+ * Uses shared EventsControls component for DRY code.
5
+ */
6
+ import React, { useState } from "react";
7
+ import type { DOEExtensionAPI } from "doe-sdk/runtime";
8
+ import { EventsControls } from "../shared";
9
+ import { ResultDisplay } from "./ResultDisplay";
10
+
11
+ export interface EventsTabProps {
12
+ api: DOEExtensionAPI;
13
+ }
14
+
15
+ export function EventsTab({ api }: EventsTabProps): React.ReactElement {
16
+ const [result, setResult] = useState<string>("");
17
+
18
+ return (
19
+ <div>
20
+ <EventsControls api={api} onResult={setResult} />
21
+ <ResultDisplay result={result} />
22
+ </div>
23
+ );
24
+ }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * InfoTab - Playground tab for Workspace and Theme info
3
+ *
4
+ * Uses shared InfoControls component for DRY code,
5
+ * plus extension-specific info display.
6
+ */
7
+ import React, { useState } from "react";
8
+ import type { DOEExtensionAPI } from "doe-sdk/runtime";
9
+ import { InfoControls } from "../shared";
10
+ import { ResultDisplay } from "./ResultDisplay";
11
+ import { spacing, colors, typography, cardStyles } from "../../styles";
12
+
13
+ export interface InfoTabProps {
14
+ api: DOEExtensionAPI;
15
+ }
16
+
17
+ export function InfoTab({ api }: InfoTabProps): React.ReactElement {
18
+ const [result, setResult] = useState<string>("");
19
+ const extensionInfo = api.extension;
20
+
21
+ return (
22
+ <div>
23
+ {/* Extension info (specific to playground) */}
24
+ <div style={{ marginBottom: spacing.md }}>
25
+ <div
26
+ style={{
27
+ fontSize: typography.sizes.sm,
28
+ fontWeight: typography.weights.medium,
29
+ color: colors.textSecondary,
30
+ marginBottom: spacing.xs,
31
+ }}
32
+ >
33
+ Extension
34
+ </div>
35
+ <div
36
+ style={{
37
+ ...cardStyles.base,
38
+ background: colors.backgroundHover,
39
+ }}
40
+ >
41
+ <div style={{ marginBottom: spacing.xs }}>
42
+ <span style={{ fontSize: typography.sizes.sm, color: colors.textSecondary }}>ID: </span>
43
+ <span
44
+ style={{
45
+ fontSize: typography.sizes.sm,
46
+ fontWeight: typography.weights.medium,
47
+ color: colors.text,
48
+ fontFamily: "monospace",
49
+ }}
50
+ >
51
+ {extensionInfo.id}
52
+ </span>
53
+ </div>
54
+ <div style={{ marginBottom: spacing.xs }}>
55
+ <span style={{ fontSize: typography.sizes.sm, color: colors.textSecondary }}>
56
+ Version:{" "}
57
+ </span>
58
+ <span
59
+ style={{
60
+ fontSize: typography.sizes.sm,
61
+ fontWeight: typography.weights.medium,
62
+ color: colors.text,
63
+ }}
64
+ >
65
+ {extensionInfo.version}
66
+ </span>
67
+ </div>
68
+ <div>
69
+ <span style={{ fontSize: typography.sizes.sm, color: colors.textSecondary }}>
70
+ Permissions:{" "}
71
+ </span>
72
+ <span
73
+ style={{
74
+ fontSize: typography.sizes.sm,
75
+ fontWeight: typography.weights.medium,
76
+ color: colors.text,
77
+ }}
78
+ >
79
+ {extensionInfo.permissions?.length || 0} granted
80
+ </span>
81
+ </div>
82
+ </div>
83
+ </div>
84
+
85
+ <InfoControls api={api} onResult={setResult} />
86
+ <ResultDisplay result={result} />
87
+ </div>
88
+ );
89
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * NetworkTab - Playground tab for Network API exploration
3
+ *
4
+ * Uses shared NetworkControls component for DRY code.
5
+ */
6
+ import React, { useState } from "react";
7
+ import type { DOEExtensionAPI } from "doe-sdk/runtime";
8
+ import { NetworkControls } from "../shared";
9
+ import { ResultDisplay } from "./ResultDisplay";
10
+
11
+ export interface NetworkTabProps {
12
+ api: DOEExtensionAPI;
13
+ }
14
+
15
+ export function NetworkTab({ api }: NetworkTabProps): React.ReactElement {
16
+ const [result, setResult] = useState<string>("");
17
+
18
+ return (
19
+ <div>
20
+ <NetworkControls api={api} onResult={setResult} />
21
+ <ResultDisplay result={result} />
22
+ </div>
23
+ );
24
+ }
@@ -0,0 +1,184 @@
1
+ /**
2
+ * PlaygroundContainer - Tabbed playground for exploring SDK APIs
3
+ *
4
+ * Post-onboarding environment for free exploration of all APIs.
5
+ */
6
+ import React, { useState, useRef } from "react";
7
+ import type { DOEExtensionAPI } from "doe-sdk/runtime";
8
+ import { useInteractivePointerDown, usePreventCanvasZoom } from "../../hooks/usePointerHandlers";
9
+ import { spacing, colors, typography, animationStyles } from "../../styles";
10
+ import { CanvasTab } from "./CanvasTab";
11
+ import { StorageTab } from "./StorageTab";
12
+ import { ConfigTab } from "./ConfigTab";
13
+ import { UITab } from "./UITab";
14
+ import { EventsTab } from "./EventsTab";
15
+ import { NetworkTab } from "./NetworkTab";
16
+ import { InfoTab } from "./InfoTab";
17
+
18
+ export interface PlaygroundContainerProps {
19
+ api: DOEExtensionAPI;
20
+ onRestartOnboarding: () => void;
21
+ }
22
+
23
+ type TabId = "canvas" | "storage" | "config" | "ui" | "events" | "network" | "info";
24
+
25
+ interface Tab {
26
+ id: TabId;
27
+ label: string;
28
+ icon: string;
29
+ }
30
+
31
+ const TABS: Tab[] = [
32
+ { id: "canvas", label: "Canvas", icon: "🎨" },
33
+ { id: "storage", label: "Storage", icon: "💾" },
34
+ { id: "config", label: "Config", icon: "⚙️" },
35
+ { id: "ui", label: "UI", icon: "🔔" },
36
+ { id: "events", label: "Events", icon: "📡" },
37
+ { id: "network", label: "Network", icon: "🌐" },
38
+ { id: "info", label: "Info", icon: "ℹ️" },
39
+ ];
40
+
41
+ export function PlaygroundContainer({
42
+ api,
43
+ onRestartOnboarding,
44
+ }: PlaygroundContainerProps): React.ReactElement {
45
+ const [activeTab, setActiveTab] = useState<TabId>("canvas");
46
+ const handlePointerDown = useInteractivePointerDown();
47
+ const contentRef = useRef<HTMLDivElement>(null);
48
+ usePreventCanvasZoom(contentRef);
49
+
50
+ const renderTabContent = () => {
51
+ switch (activeTab) {
52
+ case "canvas":
53
+ return <CanvasTab api={api} />;
54
+ case "storage":
55
+ return <StorageTab api={api} />;
56
+ case "config":
57
+ return <ConfigTab api={api} />;
58
+ case "ui":
59
+ return <UITab api={api} />;
60
+ case "events":
61
+ return <EventsTab api={api} />;
62
+ case "network":
63
+ return <NetworkTab api={api} />;
64
+ case "info":
65
+ return <InfoTab api={api} />;
66
+ default:
67
+ return null;
68
+ }
69
+ };
70
+
71
+ return (
72
+ <div
73
+ style={{
74
+ display: "flex",
75
+ flexDirection: "column",
76
+ height: "100%",
77
+ animation: "fadeIn 0.3s ease-out",
78
+ }}
79
+ >
80
+ <style>{animationStyles}</style>
81
+
82
+ {/* Header */}
83
+ <div
84
+ style={{
85
+ display: "flex",
86
+ alignItems: "center",
87
+ justifyContent: "space-between",
88
+ marginBottom: spacing.md,
89
+ }}
90
+ >
91
+ <div>
92
+ <h1
93
+ style={{
94
+ fontSize: typography.sizes.lg,
95
+ fontWeight: typography.weights.semibold,
96
+ color: colors.text,
97
+ margin: 0,
98
+ }}
99
+ >
100
+ SDK Playground
101
+ </h1>
102
+ <p
103
+ style={{
104
+ fontSize: typography.sizes.xs,
105
+ color: colors.textMuted,
106
+ margin: 0,
107
+ }}
108
+ >
109
+ Explore APIs freely
110
+ </p>
111
+ </div>
112
+ <button
113
+ onClick={onRestartOnboarding}
114
+ onPointerDown={handlePointerDown}
115
+ style={{
116
+ padding: `${spacing.xs}px ${spacing.sm}px`,
117
+ background: "transparent",
118
+ border: `1px solid ${colors.border}`,
119
+ borderRadius: 6,
120
+ fontSize: typography.sizes.xs,
121
+ color: colors.textSecondary,
122
+ cursor: "pointer",
123
+ transition: "all 0.15s ease",
124
+ }}
125
+ >
126
+ ↻ Restart Tour
127
+ </button>
128
+ </div>
129
+
130
+ {/* Tab bar */}
131
+ <div
132
+ style={{
133
+ display: "flex",
134
+ gap: spacing.xs,
135
+ marginBottom: spacing.md,
136
+ overflowX: "auto",
137
+ paddingBottom: spacing.xs,
138
+ }}
139
+ onPointerDown={handlePointerDown}
140
+ >
141
+ {TABS.map((tab) => (
142
+ <button
143
+ key={tab.id}
144
+ onClick={() => setActiveTab(tab.id)}
145
+ onPointerDown={handlePointerDown}
146
+ style={{
147
+ display: "flex",
148
+ alignItems: "center",
149
+ gap: spacing.xs,
150
+ padding: `${spacing.sm}px ${spacing.md}px`,
151
+ background: activeTab === tab.id ? colors.primary : "transparent",
152
+ color: activeTab === tab.id ? "white" : colors.textSecondary,
153
+ border: activeTab === tab.id ? "none" : `1px solid ${colors.border}`,
154
+ borderRadius: 8,
155
+ fontSize: typography.sizes.sm,
156
+ fontWeight: typography.weights.medium,
157
+ cursor: "pointer",
158
+ transition: "all 0.15s ease",
159
+ whiteSpace: "nowrap",
160
+ flexShrink: 0,
161
+ }}
162
+ >
163
+ <span>{tab.icon}</span>
164
+ {tab.label}
165
+ </button>
166
+ ))}
167
+ </div>
168
+
169
+ {/* Tab content */}
170
+ <div
171
+ ref={contentRef}
172
+ style={{
173
+ flex: 1,
174
+ overflow: "auto",
175
+ background: colors.backgroundSecondary,
176
+ borderRadius: 12,
177
+ padding: spacing.md,
178
+ }}
179
+ >
180
+ {renderTabContent()}
181
+ </div>
182
+ </div>
183
+ );
184
+ }
@@ -0,0 +1,30 @@
1
+ /**
2
+ * ResultDisplay - Shared result display for playground tabs
3
+ */
4
+ import React from "react";
5
+ import { spacing, colors, typography } from "../../styles";
6
+
7
+ export interface ResultDisplayProps {
8
+ result: string;
9
+ }
10
+
11
+ export function ResultDisplay({ result }: ResultDisplayProps): React.ReactElement | null {
12
+ if (!result) return null;
13
+
14
+ return (
15
+ <div
16
+ style={{
17
+ marginTop: spacing.md,
18
+ padding: spacing.sm,
19
+ background: colors.backgroundHover,
20
+ borderRadius: 6,
21
+ fontSize: typography.sizes.sm,
22
+ color: colors.textSecondary,
23
+ fontFamily: "monospace",
24
+ whiteSpace: "pre-wrap",
25
+ }}
26
+ >
27
+ {result}
28
+ </div>
29
+ );
30
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * StorageTab - Playground tab for Storage API exploration
3
+ *
4
+ * Uses shared StorageControls component for DRY code.
5
+ */
6
+ import React, { useState } from "react";
7
+ import type { DOEExtensionAPI } from "doe-sdk/runtime";
8
+ import { StorageControls } from "../shared";
9
+ import { ResultDisplay } from "./ResultDisplay";
10
+
11
+ export interface StorageTabProps {
12
+ api: DOEExtensionAPI;
13
+ }
14
+
15
+ export function StorageTab({ api }: StorageTabProps): React.ReactElement {
16
+ const [result, setResult] = useState<string>("");
17
+
18
+ return (
19
+ <div>
20
+ <StorageControls api={api} onResult={setResult} />
21
+ <ResultDisplay result={result} />
22
+ </div>
23
+ );
24
+ }
@@ -0,0 +1,24 @@
1
+ /**
2
+ * UITab - Playground tab for UI API exploration
3
+ *
4
+ * Uses shared UIControls component for DRY code.
5
+ */
6
+ import React, { useState } from "react";
7
+ import type { DOEExtensionAPI } from "doe-sdk/runtime";
8
+ import { UIControls } from "../shared";
9
+ import { ResultDisplay } from "./ResultDisplay";
10
+
11
+ export interface UITabProps {
12
+ api: DOEExtensionAPI;
13
+ }
14
+
15
+ export function UITab({ api }: UITabProps): React.ReactElement {
16
+ const [result, setResult] = useState<string>("");
17
+
18
+ return (
19
+ <div>
20
+ <UIControls api={api} onResult={setResult} />
21
+ <ResultDisplay result={result} />
22
+ </div>
23
+ );
24
+ }