@newtonedev/editor 0.1.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 (86) hide show
  1. package/dist/Editor.d.ts +3 -0
  2. package/dist/Editor.d.ts.map +1 -0
  3. package/dist/components/CodeBlock.d.ts +7 -0
  4. package/dist/components/CodeBlock.d.ts.map +1 -0
  5. package/dist/components/EditorHeader.d.ts +16 -0
  6. package/dist/components/EditorHeader.d.ts.map +1 -0
  7. package/dist/components/EditorShell.d.ts +10 -0
  8. package/dist/components/EditorShell.d.ts.map +1 -0
  9. package/dist/components/FontPicker.d.ts +11 -0
  10. package/dist/components/FontPicker.d.ts.map +1 -0
  11. package/dist/components/PresetSelector.d.ts +14 -0
  12. package/dist/components/PresetSelector.d.ts.map +1 -0
  13. package/dist/components/PreviewWindow.d.ts +11 -0
  14. package/dist/components/PreviewWindow.d.ts.map +1 -0
  15. package/dist/components/RightSidebar.d.ts +12 -0
  16. package/dist/components/RightSidebar.d.ts.map +1 -0
  17. package/dist/components/Sidebar.d.ts +25 -0
  18. package/dist/components/Sidebar.d.ts.map +1 -0
  19. package/dist/components/TableOfContents.d.ts +9 -0
  20. package/dist/components/TableOfContents.d.ts.map +1 -0
  21. package/dist/components/ThemeBar.d.ts +8 -0
  22. package/dist/components/ThemeBar.d.ts.map +1 -0
  23. package/dist/components/sections/ColorsSection.d.ts +14 -0
  24. package/dist/components/sections/ColorsSection.d.ts.map +1 -0
  25. package/dist/components/sections/DynamicRangeSection.d.ts +9 -0
  26. package/dist/components/sections/DynamicRangeSection.d.ts.map +1 -0
  27. package/dist/components/sections/FontsSection.d.ts +9 -0
  28. package/dist/components/sections/FontsSection.d.ts.map +1 -0
  29. package/dist/components/sections/IconsSection.d.ts +9 -0
  30. package/dist/components/sections/IconsSection.d.ts.map +1 -0
  31. package/dist/components/sections/OthersSection.d.ts +9 -0
  32. package/dist/components/sections/OthersSection.d.ts.map +1 -0
  33. package/dist/components/sections/index.d.ts +6 -0
  34. package/dist/components/sections/index.d.ts.map +1 -0
  35. package/dist/hooks/useEditorState.d.ts +53 -0
  36. package/dist/hooks/useEditorState.d.ts.map +1 -0
  37. package/dist/hooks/useHover.d.ts +8 -0
  38. package/dist/hooks/useHover.d.ts.map +1 -0
  39. package/dist/hooks/usePresets.d.ts +33 -0
  40. package/dist/hooks/usePresets.d.ts.map +1 -0
  41. package/dist/index.cjs +3846 -0
  42. package/dist/index.cjs.map +1 -0
  43. package/dist/index.d.ts +22 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +3819 -0
  46. package/dist/index.js.map +1 -0
  47. package/dist/preview/CategoryView.d.ts +7 -0
  48. package/dist/preview/CategoryView.d.ts.map +1 -0
  49. package/dist/preview/ComponentDetailView.d.ts +9 -0
  50. package/dist/preview/ComponentDetailView.d.ts.map +1 -0
  51. package/dist/preview/ComponentRenderer.d.ts +7 -0
  52. package/dist/preview/ComponentRenderer.d.ts.map +1 -0
  53. package/dist/preview/OverviewView.d.ts +7 -0
  54. package/dist/preview/OverviewView.d.ts.map +1 -0
  55. package/dist/types.d.ts +69 -0
  56. package/dist/types.d.ts.map +1 -0
  57. package/dist/utils/presets.d.ts +5 -0
  58. package/dist/utils/presets.d.ts.map +1 -0
  59. package/package.json +51 -0
  60. package/src/Editor.tsx +128 -0
  61. package/src/components/CodeBlock.tsx +58 -0
  62. package/src/components/EditorHeader.tsx +86 -0
  63. package/src/components/EditorShell.tsx +67 -0
  64. package/src/components/FontPicker.tsx +351 -0
  65. package/src/components/PresetSelector.tsx +455 -0
  66. package/src/components/PreviewWindow.tsx +69 -0
  67. package/src/components/RightSidebar.tsx +374 -0
  68. package/src/components/Sidebar.tsx +332 -0
  69. package/src/components/TableOfContents.tsx +152 -0
  70. package/src/components/ThemeBar.tsx +76 -0
  71. package/src/components/sections/ColorsSection.tsx +485 -0
  72. package/src/components/sections/DynamicRangeSection.tsx +399 -0
  73. package/src/components/sections/FontsSection.tsx +132 -0
  74. package/src/components/sections/IconsSection.tsx +66 -0
  75. package/src/components/sections/OthersSection.tsx +70 -0
  76. package/src/components/sections/index.ts +5 -0
  77. package/src/hooks/useEditorState.ts +381 -0
  78. package/src/hooks/useHover.ts +8 -0
  79. package/src/hooks/usePresets.ts +254 -0
  80. package/src/index.ts +52 -0
  81. package/src/preview/CategoryView.tsx +134 -0
  82. package/src/preview/ComponentDetailView.tsx +126 -0
  83. package/src/preview/ComponentRenderer.tsx +107 -0
  84. package/src/preview/OverviewView.tsx +177 -0
  85. package/src/types.ts +77 -0
  86. package/src/utils/presets.ts +24 -0
@@ -0,0 +1,7 @@
1
+ interface CategoryViewProps {
2
+ readonly categoryId: string;
3
+ readonly onNavigateToComponent: (componentId: string) => void;
4
+ }
5
+ export declare function CategoryView({ categoryId, onNavigateToComponent, }: CategoryViewProps): import("react/jsx-runtime").JSX.Element | null;
6
+ export {};
7
+ //# sourceMappingURL=CategoryView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CategoryView.d.ts","sourceRoot":"","sources":["../../src/preview/CategoryView.tsx"],"names":[],"mappings":"AAMA,UAAU,iBAAiB;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,qBAAqB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/D;AAED,wBAAgB,YAAY,CAAC,EAC3B,UAAU,EACV,qBAAqB,GACtB,EAAE,iBAAiB,kDAuHnB"}
@@ -0,0 +1,9 @@
1
+ interface ComponentDetailViewProps {
2
+ readonly componentId: string;
3
+ readonly selectedVariantId: string | null;
4
+ readonly propOverrides?: Record<string, unknown>;
5
+ readonly onSelectVariant: (variantId: string) => void;
6
+ }
7
+ export declare function ComponentDetailView({ componentId, selectedVariantId, propOverrides, onSelectVariant, }: ComponentDetailViewProps): import("react/jsx-runtime").JSX.Element | null;
8
+ export {};
9
+ //# sourceMappingURL=ComponentDetailView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ComponentDetailView.d.ts","sourceRoot":"","sources":["../../src/preview/ComponentDetailView.tsx"],"names":[],"mappings":"AAMA,UAAU,wBAAwB;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjD,QAAQ,CAAC,eAAe,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;CACvD;AAED,wBAAgB,mBAAmB,CAAC,EAClC,WAAW,EACX,iBAAiB,EACjB,aAAa,EACb,eAAe,GAChB,EAAE,wBAAwB,kDA2G1B"}
@@ -0,0 +1,7 @@
1
+ interface ComponentRendererProps {
2
+ readonly componentId: string;
3
+ readonly props: Record<string, unknown>;
4
+ }
5
+ export declare function ComponentRenderer({ componentId, props }: ComponentRendererProps): import("react/jsx-runtime").JSX.Element | null;
6
+ export {};
7
+ //# sourceMappingURL=ComponentRenderer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ComponentRenderer.d.ts","sourceRoot":"","sources":["../../src/preview/ComponentRenderer.tsx"],"names":[],"mappings":"AAaA,UAAU,sBAAsB;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC;AAyDD,wBAAgB,iBAAiB,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,sBAAsB,kDAiC/E"}
@@ -0,0 +1,7 @@
1
+ interface OverviewViewProps {
2
+ readonly onNavigateToCategory: (categoryId: string) => void;
3
+ readonly onNavigateToComponent: (componentId: string) => void;
4
+ }
5
+ export declare function OverviewView({ onNavigateToCategory, onNavigateToComponent, }: OverviewViewProps): import("react/jsx-runtime").JSX.Element;
6
+ export {};
7
+ //# sourceMappingURL=OverviewView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OverviewView.d.ts","sourceRoot":"","sources":["../../src/preview/OverviewView.tsx"],"names":[],"mappings":"AAMA,UAAU,iBAAiB;IACzB,QAAQ,CAAC,oBAAoB,EAAE,CAAC,UAAU,EAAE,MAAM,KAAK,IAAI,CAAC;IAC5D,QAAQ,CAAC,qBAAqB,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/D;AAED,wBAAgB,YAAY,CAAC,EAC3B,oBAAoB,EACpB,qBAAqB,GACtB,EAAE,iBAAiB,2CAwFnB"}
@@ -0,0 +1,69 @@
1
+ import type { ConfiguratorState } from "@newtonedev/configurator";
2
+ import type { NewtoneThemeConfig } from "@newtonedev/components";
3
+ import type { ReactNode } from "react";
4
+ export interface Preset {
5
+ readonly id: string;
6
+ readonly name: string;
7
+ readonly draft_state: ConfiguratorState;
8
+ readonly published_state: ConfiguratorState | null;
9
+ }
10
+ export type SaveStatus = "saved" | "saving" | "unsaved" | "error";
11
+ export type ThemeName = "neutral" | "primary" | "secondary" | "strong";
12
+ export type PreviewView = {
13
+ readonly kind: "overview";
14
+ } | {
15
+ readonly kind: "category";
16
+ readonly categoryId: string;
17
+ } | {
18
+ readonly kind: "component";
19
+ readonly componentId: string;
20
+ };
21
+ export type SidebarSelection = null | {
22
+ readonly scope: "component";
23
+ readonly componentId: string;
24
+ } | {
25
+ readonly scope: "variant";
26
+ readonly componentId: string;
27
+ readonly variantId: string;
28
+ };
29
+ export interface EditorPersistence {
30
+ /** Save draft state. Called on 2s debounce. */
31
+ readonly onSaveDraft: (params: {
32
+ readonly state: ConfiguratorState;
33
+ readonly presets: readonly Preset[];
34
+ }) => Promise<{
35
+ error?: unknown;
36
+ }>;
37
+ /** Publish the active preset. */
38
+ readonly onPublish: (params: {
39
+ readonly state: ConfiguratorState;
40
+ readonly presets: readonly Preset[];
41
+ readonly activePresetId: string;
42
+ }) => Promise<{
43
+ error?: unknown;
44
+ }>;
45
+ /** Persist preset metadata (used by preset CRUD operations). */
46
+ readonly persistPresets: (params: {
47
+ readonly presets: readonly Preset[];
48
+ readonly activePresetId: string;
49
+ readonly publishedPresetId: string | null;
50
+ }) => Promise<void>;
51
+ }
52
+ export interface EditorHeaderSlots {
53
+ readonly left?: ReactNode;
54
+ readonly right?: ReactNode;
55
+ }
56
+ export interface EditorProps {
57
+ readonly initialState: ConfiguratorState;
58
+ readonly initialIsPublished: boolean;
59
+ readonly initialPresets: readonly Preset[];
60
+ readonly initialActivePresetId: string;
61
+ readonly initialPublishedPresetId: string | null;
62
+ readonly defaultState: ConfiguratorState;
63
+ readonly chromeThemeConfig: NewtoneThemeConfig;
64
+ readonly persistence: EditorPersistence;
65
+ readonly headerSlots?: EditorHeaderSlots;
66
+ readonly onNavigate?: (view: PreviewView) => void;
67
+ readonly initialPreviewView?: PreviewView;
68
+ }
69
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,5 @@
1
+ import type { Preset } from "../types";
2
+ export declare function findPreset(presets: readonly Preset[], presetId: string): Preset | undefined;
3
+ export declare function updatePresetInArray(presets: readonly Preset[], presetId: string, updater: (preset: Preset) => Preset): readonly Preset[];
4
+ export declare function presetHasUnpublishedChanges(preset: Preset): boolean;
5
+ //# sourceMappingURL=presets.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../src/utils/presets.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AAEvC,wBAAgB,UAAU,CACxB,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,QAAQ,EAAE,MAAM,GACf,MAAM,GAAG,SAAS,CAEpB;AAED,wBAAgB,mBAAmB,CACjC,OAAO,EAAE,SAAS,MAAM,EAAE,EAC1B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,GAClC,SAAS,MAAM,EAAE,CAEnB;AAED,wBAAgB,2BAA2B,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAMnE"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@newtonedev/editor",
3
+ "version": "0.1.1",
4
+ "description": "Shared color system editor for Newtone applications",
5
+ "license": "MIT",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/joshuaallenmx/newtone-api.git",
9
+ "directory": "packages/editor"
10
+ },
11
+ "homepage": "https://newtone.dev",
12
+ "type": "module",
13
+ "main": "./dist/index.cjs",
14
+ "module": "./dist/index.js",
15
+ "types": "./dist/index.d.ts",
16
+ "react-native": "./src/index.ts",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/index.d.ts",
20
+ "react-native": "./src/index.ts",
21
+ "import": "./dist/index.js",
22
+ "require": "./dist/index.cjs"
23
+ }
24
+ },
25
+ "files": [
26
+ "dist",
27
+ "src"
28
+ ],
29
+ "scripts": {
30
+ "build": "tsup && rm -f tsconfig.tsbuildinfo && tsc --emitDeclarationOnly",
31
+ "dev": "tsup --watch",
32
+ "typecheck": "tsc --noEmit"
33
+ },
34
+ "peerDependencies": {
35
+ "react": ">=18.0.0",
36
+ "react-native": ">=0.70.0"
37
+ },
38
+ "dependencies": {
39
+ "newtone": "^0.1.0",
40
+ "@newtonedev/components": "^0.1.0",
41
+ "@newtonedev/configurator": "^0.1.0",
42
+ "react-native-web": "^0.19.10"
43
+ },
44
+ "devDependencies": {
45
+ "@types/react": "^18.2.0",
46
+ "react": "^18.2.0",
47
+ "react-dom": "^18.2.0",
48
+ "tsup": "^8.0.0",
49
+ "typescript": "^5.4.0"
50
+ }
51
+ }
package/src/Editor.tsx ADDED
@@ -0,0 +1,128 @@
1
+ import { NewtoneProvider } from "@newtonedev/components";
2
+ import { useEditorState } from "./hooks/useEditorState";
3
+ import { EditorShell } from "./components/EditorShell";
4
+ import { Sidebar } from "./components/Sidebar";
5
+ import { EditorHeader } from "./components/EditorHeader";
6
+ import { ThemeBar } from "./components/ThemeBar";
7
+ import { TableOfContents } from "./components/TableOfContents";
8
+ import { PreviewWindow } from "./components/PreviewWindow";
9
+ import { RightSidebar } from "./components/RightSidebar";
10
+ import type { EditorProps } from "./types";
11
+
12
+ export function Editor({
13
+ initialState,
14
+ initialIsPublished,
15
+ initialPresets,
16
+ initialActivePresetId,
17
+ initialPublishedPresetId,
18
+ defaultState,
19
+ chromeThemeConfig,
20
+ persistence,
21
+ headerSlots,
22
+ onNavigate,
23
+ initialPreviewView,
24
+ }: EditorProps) {
25
+ const editor = useEditorState({
26
+ initialState,
27
+ initialIsPublished,
28
+ initialPresets,
29
+ initialActivePresetId,
30
+ initialPublishedPresetId,
31
+ defaultState,
32
+ persistence,
33
+ onNavigate,
34
+ initialPreviewView,
35
+ });
36
+
37
+ return (
38
+ <NewtoneProvider config={chromeThemeConfig}>
39
+ <EditorShell
40
+ sidebar={
41
+ <Sidebar
42
+ state={editor.configuratorState}
43
+ dispatch={editor.dispatch}
44
+ previewColors={editor.previewColors}
45
+ isDirty={editor.isDirty}
46
+ onRevert={editor.handleRevert}
47
+ presets={editor.presets}
48
+ activePresetId={editor.activePresetId}
49
+ publishedPresetId={editor.publishedPresetId}
50
+ onSwitchPreset={editor.switchPreset}
51
+ onCreatePreset={editor.createPreset}
52
+ onRenamePreset={editor.renamePreset}
53
+ onDeletePreset={editor.deletePreset}
54
+ onDuplicatePreset={editor.duplicatePreset}
55
+ colorMode={editor.colorMode}
56
+ onColorModeChange={editor.handleColorModeChange}
57
+ />
58
+ }
59
+ navbar={
60
+ <EditorHeader
61
+ saveStatus={editor.saveStatus}
62
+ isPublished={editor.isPublished}
63
+ publishing={editor.publishing}
64
+ onPublish={editor.handlePublish}
65
+ onRetry={() => editor.saveDraft(editor.latestStateRef.current)}
66
+ headerSlots={headerSlots}
67
+ />
68
+ }
69
+ content={
70
+ <div
71
+ style={{
72
+ flex: 1,
73
+ display: "flex",
74
+ overflow: "hidden",
75
+ minWidth: 0,
76
+ }}
77
+ >
78
+ <TableOfContents
79
+ activeView={editor.previewView}
80
+ selectedComponentId={editor.selectedComponentId}
81
+ onNavigate={editor.handlePreviewNavigate}
82
+ />
83
+ <div
84
+ style={{
85
+ flex: 1,
86
+ display: "flex",
87
+ flexDirection: "column",
88
+ overflow: "hidden",
89
+ minWidth: 0,
90
+ }}
91
+ >
92
+ <NewtoneProvider
93
+ config={editor.themeConfig}
94
+ initialMode={editor.colorMode}
95
+ initialTheme={editor.activeTheme}
96
+ key={`${editor.colorMode}-${editor.activeTheme}`}
97
+ >
98
+ <ThemeBar
99
+ activeTheme={editor.activeTheme}
100
+ onThemeChange={editor.handleThemeChange}
101
+ />
102
+ <div style={{ flex: 1, overflowY: "auto", minWidth: 0 }}>
103
+ <PreviewWindow
104
+ view={editor.previewView}
105
+ selectedVariantId={editor.selectedVariantId}
106
+ propOverrides={editor.propOverrides}
107
+ onNavigate={editor.handlePreviewNavigate}
108
+ onSelectVariant={editor.handleSelectVariant}
109
+ />
110
+ </div>
111
+ </NewtoneProvider>
112
+ </div>
113
+ </div>
114
+ }
115
+ rightPanel={
116
+ <RightSidebar
117
+ selection={editor.sidebarSelection}
118
+ propOverrides={editor.propOverrides}
119
+ onPropOverride={editor.handlePropOverride}
120
+ onResetOverrides={editor.handleResetOverrides}
121
+ onClose={editor.handleCloseSidebar}
122
+ onScopeToComponent={editor.handleScopeToComponent}
123
+ />
124
+ }
125
+ />
126
+ </NewtoneProvider>
127
+ );
128
+ }
@@ -0,0 +1,58 @@
1
+ import { useState, useCallback } from "react";
2
+ import { useTokens, Button } from "@newtonedev/components";
3
+ import { srgbToHex } from "newtone";
4
+
5
+ export function CopyButton({ text }: { readonly text: string }) {
6
+ const [copied, setCopied] = useState(false);
7
+
8
+ const handleCopy = useCallback(async () => {
9
+ await navigator.clipboard.writeText(text);
10
+ setCopied(true);
11
+ setTimeout(() => setCopied(false), 2000);
12
+ }, [text]);
13
+
14
+ return (
15
+ <Button variant="ghost" size="sm" icon={copied ? "check" : "content_copy"} onPress={handleCopy}>
16
+ {copied ? "Copied!" : "Copy"}
17
+ </Button>
18
+ );
19
+ }
20
+
21
+ export function CodeBlock({
22
+ code,
23
+ }: {
24
+ readonly code: string;
25
+ }) {
26
+ const tokens = useTokens();
27
+
28
+ return (
29
+ <div style={{ position: "relative" }}>
30
+ <div
31
+ style={{
32
+ position: "absolute",
33
+ top: 8,
34
+ right: 8,
35
+ }}
36
+ >
37
+ <CopyButton text={code} />
38
+ </div>
39
+ <pre
40
+ style={{
41
+ backgroundColor: srgbToHex(tokens.backgroundSunken.srgb),
42
+ border: `1px solid ${srgbToHex(tokens.border.srgb)}`,
43
+ borderRadius: 8,
44
+ padding: 16,
45
+ paddingRight: 80,
46
+ overflow: "auto",
47
+ fontSize: 13,
48
+ lineHeight: 1.5,
49
+ fontFamily: "'SF Mono', 'Fira Code', 'Fira Mono', Menlo, monospace",
50
+ color: srgbToHex(tokens.textPrimary.srgb),
51
+ margin: 0,
52
+ }}
53
+ >
54
+ <code>{code}</code>
55
+ </pre>
56
+ </div>
57
+ );
58
+ }
@@ -0,0 +1,86 @@
1
+ import type { ReactNode } from "react";
2
+ import { useTokens, Button } from "@newtonedev/components";
3
+ import { srgbToHex } from "newtone";
4
+ import type { SaveStatus } from "../types";
5
+
6
+ interface EditorHeaderProps {
7
+ readonly saveStatus: SaveStatus;
8
+ readonly isPublished: boolean;
9
+ readonly publishing: boolean;
10
+ readonly onPublish: () => void;
11
+ readonly onRetry: () => void;
12
+ readonly headerSlots?: {
13
+ readonly left?: ReactNode;
14
+ readonly right?: ReactNode;
15
+ };
16
+ }
17
+
18
+ const STATUS_LABEL: Record<SaveStatus, string> = {
19
+ saved: "Saved",
20
+ saving: "Saving...",
21
+ unsaved: "Unsaved changes",
22
+ error: "Save failed",
23
+ };
24
+
25
+ export function EditorHeader({
26
+ saveStatus,
27
+ isPublished,
28
+ publishing,
29
+ onPublish,
30
+ onRetry,
31
+ headerSlots,
32
+ }: EditorHeaderProps) {
33
+ const tokens = useTokens();
34
+ const borderColor = srgbToHex(tokens.border.srgb);
35
+
36
+ const statusColor: Record<SaveStatus, string> = {
37
+ saved: srgbToHex(tokens.success.srgb),
38
+ saving: srgbToHex(tokens.warning.srgb),
39
+ unsaved: srgbToHex(tokens.textSecondary.srgb),
40
+ error: srgbToHex(tokens.error.srgb),
41
+ };
42
+
43
+ return (
44
+ <div
45
+ style={{
46
+ display: "flex",
47
+ alignItems: "center",
48
+ justifyContent: "space-between",
49
+ padding: "12px 24px",
50
+ borderBottom: `1px solid ${borderColor}`,
51
+ backgroundColor: srgbToHex(tokens.background.srgb),
52
+ flexShrink: 0,
53
+ }}
54
+ >
55
+ <div style={{ display: "flex", alignItems: "center", gap: 16 }}>
56
+ {headerSlots?.left}
57
+ </div>
58
+ <div style={{ display: "flex", alignItems: "center", gap: 12 }}>
59
+ <span
60
+ style={{
61
+ fontSize: 12,
62
+ color: statusColor[saveStatus],
63
+ fontWeight: 500,
64
+ }}
65
+ >
66
+ {STATUS_LABEL[saveStatus]}
67
+ </span>
68
+ {saveStatus === "error" && (
69
+ <Button variant="ghost" size="sm" icon="refresh" onPress={onRetry}>
70
+ Retry
71
+ </Button>
72
+ )}
73
+ <Button
74
+ variant="primary"
75
+ size="sm"
76
+ icon="publish"
77
+ onPress={onPublish}
78
+ disabled={isPublished || publishing}
79
+ >
80
+ {publishing ? "Publishing..." : isPublished ? "Published" : "Publish"}
81
+ </Button>
82
+ {headerSlots?.right}
83
+ </div>
84
+ </div>
85
+ );
86
+ }
@@ -0,0 +1,67 @@
1
+ import type { ReactNode } from "react";
2
+ import { useTokens } from "@newtonedev/components";
3
+ import { srgbToHex } from "newtone";
4
+
5
+ const SIDEBAR_WIDTH = 360;
6
+
7
+ interface EditorShellProps {
8
+ readonly sidebar: ReactNode;
9
+ readonly navbar: ReactNode;
10
+ readonly content: ReactNode;
11
+ readonly rightPanel: ReactNode;
12
+ }
13
+
14
+ export function EditorShell({
15
+ sidebar,
16
+ navbar,
17
+ content,
18
+ rightPanel,
19
+ }: EditorShellProps) {
20
+ const tokens = useTokens();
21
+
22
+ return (
23
+ <div
24
+ style={{
25
+ display: "flex",
26
+ height: "100vh",
27
+ overflow: "hidden",
28
+ backgroundColor: srgbToHex(tokens.background.srgb),
29
+ }}
30
+ >
31
+ <div
32
+ style={{
33
+ flex: 1,
34
+ display: "flex",
35
+ flexDirection: "column",
36
+ height: "100vh",
37
+ overflow: "hidden",
38
+ minWidth: 0,
39
+ }}
40
+ >
41
+ {navbar}
42
+
43
+ <div
44
+ style={{
45
+ display: "flex",
46
+ flex: 1,
47
+ overflow: "hidden",
48
+ }}
49
+ >
50
+ {content}
51
+ </div>
52
+ </div>
53
+
54
+ <div
55
+ style={{
56
+ position: "relative",
57
+ width: SIDEBAR_WIDTH,
58
+ flexShrink: 0,
59
+ overflow: "hidden",
60
+ }}
61
+ >
62
+ {sidebar}
63
+ {rightPanel}
64
+ </div>
65
+ </div>
66
+ );
67
+ }