@newtonedev/editor 0.1.6 → 0.1.8

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 (52) hide show
  1. package/dist/Editor.d.ts +1 -1
  2. package/dist/Editor.d.ts.map +1 -1
  3. package/dist/components/ConfiguratorPanel.d.ts +17 -0
  4. package/dist/components/ConfiguratorPanel.d.ts.map +1 -0
  5. package/dist/components/FontPicker.d.ts +4 -2
  6. package/dist/components/FontPicker.d.ts.map +1 -1
  7. package/dist/components/PreviewWindow.d.ts +7 -2
  8. package/dist/components/PreviewWindow.d.ts.map +1 -1
  9. package/dist/components/PrimaryNav.d.ts +7 -0
  10. package/dist/components/PrimaryNav.d.ts.map +1 -0
  11. package/dist/components/Sidebar.d.ts +1 -10
  12. package/dist/components/Sidebar.d.ts.map +1 -1
  13. package/dist/components/TableOfContents.d.ts +2 -1
  14. package/dist/components/TableOfContents.d.ts.map +1 -1
  15. package/dist/components/sections/DynamicRangeSection.d.ts.map +1 -1
  16. package/dist/components/sections/FontsSection.d.ts +3 -1
  17. package/dist/components/sections/FontsSection.d.ts.map +1 -1
  18. package/dist/hooks/useEditorState.d.ts +4 -1
  19. package/dist/hooks/useEditorState.d.ts.map +1 -1
  20. package/dist/index.cjs +2484 -2052
  21. package/dist/index.cjs.map +1 -1
  22. package/dist/index.d.ts +2 -1
  23. package/dist/index.d.ts.map +1 -1
  24. package/dist/index.js +2486 -2055
  25. package/dist/index.js.map +1 -1
  26. package/dist/preview/ComponentDetailView.d.ts +7 -1
  27. package/dist/preview/ComponentDetailView.d.ts.map +1 -1
  28. package/dist/preview/ComponentRenderer.d.ts +2 -1
  29. package/dist/preview/ComponentRenderer.d.ts.map +1 -1
  30. package/dist/types.d.ts +17 -0
  31. package/dist/types.d.ts.map +1 -1
  32. package/dist/utils/lookupFontMetrics.d.ts +19 -0
  33. package/dist/utils/lookupFontMetrics.d.ts.map +1 -0
  34. package/dist/utils/measureFonts.d.ts +18 -0
  35. package/dist/utils/measureFonts.d.ts.map +1 -0
  36. package/package.json +1 -1
  37. package/src/Editor.tsx +53 -10
  38. package/src/components/ConfiguratorPanel.tsx +77 -0
  39. package/src/components/FontPicker.tsx +38 -29
  40. package/src/components/PreviewWindow.tsx +14 -1
  41. package/src/components/PrimaryNav.tsx +76 -0
  42. package/src/components/Sidebar.tsx +5 -132
  43. package/src/components/TableOfContents.tsx +41 -78
  44. package/src/components/sections/DynamicRangeSection.tsx +2 -225
  45. package/src/components/sections/FontsSection.tsx +61 -93
  46. package/src/hooks/useEditorState.ts +68 -17
  47. package/src/index.ts +2 -0
  48. package/src/preview/ComponentDetailView.tsx +531 -67
  49. package/src/preview/ComponentRenderer.tsx +6 -4
  50. package/src/types.ts +15 -0
  51. package/src/utils/lookupFontMetrics.ts +52 -0
  52. package/src/utils/measureFonts.ts +41 -0
@@ -1,10 +1,16 @@
1
+ import type { TextRole } from "@newtonedev/fonts";
2
+ import type { EditorFontEntry } from "../types";
1
3
  interface ComponentDetailViewProps {
2
4
  readonly componentId: string;
3
5
  readonly selectedVariantId: string | null;
4
6
  readonly onSelectVariant: (variantId: string) => void;
5
7
  readonly propOverrides?: Record<string, unknown>;
6
8
  readonly onPropOverride?: (name: string, value: unknown) => void;
9
+ readonly roleWeights?: Partial<Record<TextRole, number>>;
10
+ readonly onRoleWeightChange?: (role: TextRole, weight: number) => void;
11
+ readonly fontCatalog?: readonly EditorFontEntry[];
12
+ readonly scopeFontMap?: Record<string, string>;
7
13
  }
8
- export declare function ComponentDetailView({ componentId, selectedVariantId, onSelectVariant, propOverrides, onPropOverride, }: ComponentDetailViewProps): import("react/jsx-runtime").JSX.Element | null;
14
+ export declare function ComponentDetailView({ componentId, selectedVariantId, onSelectVariant, propOverrides, onPropOverride, roleWeights, onRoleWeightChange, fontCatalog, scopeFontMap, }: ComponentDetailViewProps): import("react/jsx-runtime").JSX.Element | null;
9
15
  export {};
10
16
  //# sourceMappingURL=ComponentDetailView.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ComponentDetailView.d.ts","sourceRoot":"","sources":["../../src/preview/ComponentDetailView.tsx"],"names":[],"mappings":"AAOA,UAAU,wBAAwB;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,QAAQ,CAAC,eAAe,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACtD,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjD,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;CAClE;AAED,wBAAgB,mBAAmB,CAAC,EAClC,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,cAAc,GACf,EAAE,wBAAwB,kDA+I1B"}
1
+ {"version":3,"file":"ComponentDetailView.d.ts","sourceRoot":"","sources":["../../src/preview/ComponentDetailView.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAuC,MAAM,mBAAmB,CAAC;AAIvF,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,UAAU,CAAC;AAahD,UAAU,wBAAwB;IAChC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1C,QAAQ,CAAC,eAAe,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAC;IACtD,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjD,QAAQ,CAAC,cAAc,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,KAAK,IAAI,CAAC;IACjE,QAAQ,CAAC,WAAW,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;IACzD,QAAQ,CAAC,kBAAkB,CAAC,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACvE,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;IAClD,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChD;AA+PD,wBAAgB,mBAAmB,CAAC,EAClC,WAAW,EACX,iBAAiB,EACjB,eAAe,EACf,aAAa,EACb,cAAc,EACd,WAAW,EACX,kBAAkB,EAClB,WAAW,EACX,YAAY,GACb,EAAE,wBAAwB,kDA4U1B"}
@@ -1,7 +1,8 @@
1
1
  interface ComponentRendererProps {
2
2
  readonly componentId: string;
3
3
  readonly props: Record<string, unknown>;
4
+ readonly previewText?: string;
4
5
  }
5
- export declare function ComponentRenderer({ componentId, props }: ComponentRendererProps): import("react/jsx-runtime").JSX.Element | null;
6
+ export declare function ComponentRenderer({ componentId, props, previewText }: ComponentRendererProps): import("react/jsx-runtime").JSX.Element | null;
6
7
  export {};
7
8
  //# sourceMappingURL=ComponentRenderer.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ComponentRenderer.d.ts","sourceRoot":"","sources":["../../src/preview/ComponentRenderer.tsx"],"names":[],"mappings":"AAiBA,UAAU,sBAAsB;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACzC;AA2ED,wBAAgB,iBAAiB,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,EAAE,sBAAsB,kDA8D/E"}
1
+ {"version":3,"file":"ComponentRenderer.d.ts","sourceRoot":"","sources":["../../src/preview/ComponentRenderer.tsx"],"names":[],"mappings":"AAiBA,UAAU,sBAAsB;IAC9B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACxC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AA2ED,wBAAgB,iBAAiB,CAAC,EAAE,WAAW,EAAE,KAAK,EAAE,WAAW,EAAE,EAAE,sBAAsB,kDA+D5F"}
package/dist/types.d.ts CHANGED
@@ -1,6 +1,17 @@
1
1
  import type { ConfiguratorState } from "@newtonedev/configurator";
2
2
  import type { NewtoneThemeConfig } from "@newtonedev/components";
3
+ import type { FontRuntimeMetrics, GoogleFontEntry } from "@newtonedev/fonts";
3
4
  import type { ReactNode } from "react";
5
+ /** Font catalog entry enriched with weight metadata for the editor. */
6
+ export interface EditorFontEntry extends GoogleFontEntry {
7
+ readonly isVariable?: boolean;
8
+ readonly availableWeights?: readonly number[];
9
+ /** Weight axis range for variable fonts (from Google Fonts API wght axis). */
10
+ readonly weightAxisRange?: {
11
+ readonly min: number;
12
+ readonly max: number;
13
+ };
14
+ }
4
15
  export interface Preset {
5
16
  readonly id: string;
6
17
  readonly name: string;
@@ -38,6 +49,8 @@ export interface EditorPersistence {
38
49
  readonly state: ConfiguratorState;
39
50
  readonly presets: readonly Preset[];
40
51
  readonly activePresetId: string;
52
+ readonly calibrations?: Record<string, number>;
53
+ readonly fontMetrics?: Record<string, FontRuntimeMetrics>;
41
54
  }) => Promise<{
42
55
  error?: unknown;
43
56
  }>;
@@ -64,5 +77,9 @@ export interface EditorProps {
64
77
  readonly headerSlots?: EditorHeaderSlots;
65
78
  readonly onNavigate?: (view: PreviewView) => void;
66
79
  readonly initialPreviewView?: PreviewView;
80
+ /** URL of the font manifest for metrics lookup at publish time. */
81
+ readonly manifestUrl?: string;
82
+ /** Curated fonts available in the font picker, enriched with weight metadata. */
83
+ readonly fontCatalog?: readonly EditorFontEntry[];
67
84
  }
68
85
  //# sourceMappingURL=types.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import type { FontScope } from '@newtonedev/fonts';
2
+ import type { FontSlot } from '@newtonedev/components';
3
+ import type { FontRuntimeMetrics } from '@newtonedev/fonts';
4
+ /**
5
+ * Look up FontRuntimeMetrics for all font scopes from the font manifest.
6
+ *
7
+ * Called at publish time alongside measureFontCalibrations. Fetches the
8
+ * manifest JSON from the given URL and extracts metrics for each unique
9
+ * font family in the current typography configuration.
10
+ *
11
+ * Deduplicates by family name so a font used in multiple scopes is
12
+ * looked up only once. Returns empty object if manifest is unavailable.
13
+ *
14
+ * @param fonts - The typography.fonts record from ConfiguratorState.
15
+ * @param manifestUrl - URL of the font manifest (e.g., Supabase Storage public URL).
16
+ * @returns Map of fontFamily → FontRuntimeMetrics.
17
+ */
18
+ export declare function lookupFontMetrics(fonts: Record<FontScope, FontSlot> | undefined, manifestUrl: string | undefined): Promise<Record<string, FontRuntimeMetrics>>;
19
+ //# sourceMappingURL=lookupFontMetrics.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lookupFontMetrics.d.ts","sourceRoot":"","sources":["../../src/utils/lookupFontMetrics.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AACvD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAE5D;;;;;;;;;;;;;GAaG;AACH,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,GAAG,SAAS,EAC9C,WAAW,EAAE,MAAM,GAAG,SAAS,GAC9B,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CA8B7C"}
@@ -0,0 +1,18 @@
1
+ import type { FontScope } from '@newtonedev/fonts';
2
+ import type { FontSlot } from '@newtonedev/components';
3
+ /**
4
+ * Measure avgCharWidth ratios for all font scopes at publish time.
5
+ *
6
+ * Deduplicates by font family name so a font used in multiple scopes is
7
+ * measured only once. Waits for fonts to load via `document.fonts.ready`
8
+ * before measuring, since the editor always preloads fonts for preview.
9
+ *
10
+ * Called in `handlePublish` before writing to persistence so that calibration
11
+ * data is included in the published config served to consumer sites.
12
+ *
13
+ * @param fonts - The typography.fonts record from ConfiguratorState.
14
+ * @returns Map of fontFamily → avgCharWidthRatio (e.g. `{ "Inter": 0.52 }`).
15
+ * Returns empty object if called outside a browser context.
16
+ */
17
+ export declare function measureFontCalibrations(fonts: Record<FontScope, FontSlot> | undefined): Promise<Record<string, number>>;
18
+ //# sourceMappingURL=measureFonts.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"measureFonts.d.ts","sourceRoot":"","sources":["../../src/utils/measureFonts.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAC;AACnD,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAGvD;;;;;;;;;;;;;GAaG;AACH,wBAAsB,uBAAuB,CAC3C,KAAK,EAAE,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,GAAG,SAAS,GAC7C,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAoBjC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@newtonedev/editor",
3
- "version": "0.1.6",
3
+ "version": "0.1.8",
4
4
  "description": "Shared color system editor for Newtone applications",
5
5
  "license": "MIT",
6
6
  "repository": {
package/src/Editor.tsx CHANGED
@@ -1,10 +1,13 @@
1
- import { useMemo } from "react";
1
+ import { useMemo, useCallback } from "react";
2
2
  import { NewtoneProvider } from "@newtonedev/components";
3
3
  import type { NewtoneThemeConfig } from "@newtonedev/components";
4
+ import type { TextRole } from "@newtonedev/fonts";
4
5
  import { useEditorState } from "./hooks/useEditorState";
5
6
  import { EditorShell } from "./components/EditorShell";
6
7
  import { Sidebar } from "./components/Sidebar";
7
8
  import { EditorHeader } from "./components/EditorHeader";
9
+ import { PrimaryNav } from "./components/PrimaryNav";
10
+ import { ConfiguratorPanel } from "./components/ConfiguratorPanel";
8
11
  import { TableOfContents } from "./components/TableOfContents";
9
12
  import { PreviewWindow } from "./components/PreviewWindow";
10
13
  import { RightSidebar } from "./components/RightSidebar";
@@ -22,6 +25,8 @@ export function Editor({
22
25
  headerSlots,
23
26
  onNavigate,
24
27
  initialPreviewView,
28
+ manifestUrl,
29
+ fontCatalog,
25
30
  }: EditorProps) {
26
31
  const editor = useEditorState({
27
32
  initialState,
@@ -33,8 +38,30 @@ export function Editor({
33
38
  persistence,
34
39
  onNavigate,
35
40
  initialPreviewView,
41
+ manifestUrl,
36
42
  });
37
43
 
44
+ // Per-role weight state and handler
45
+ const roleWeights = editor.configuratorState.typography?.roleWeights;
46
+ const handleRoleWeightChange = useCallback(
47
+ (role: TextRole, weight: number) => {
48
+ editor.dispatch({ type: "SET_ROLE_WEIGHT", role, weight });
49
+ },
50
+ [editor.dispatch],
51
+ );
52
+
53
+ // Map font scopes to family names for weight metadata lookup
54
+ const scopeFontMap = useMemo((): Record<string, string> => {
55
+ const fonts = editor.configuratorState.typography?.fonts;
56
+ if (!fonts) return {};
57
+ const map: Record<string, string> = {};
58
+ if (fonts.main?.config?.family) map.main = fonts.main.config.family;
59
+ if (fonts.display?.config?.family) map.display = fonts.display.config.family;
60
+ if (fonts.mono?.config?.family) map.mono = fonts.mono.config.family;
61
+ if (fonts.currency?.config?.family) map.currency = fonts.currency.config.family;
62
+ return map;
63
+ }, [editor.configuratorState.typography?.fonts]);
64
+
38
65
  // Merge token overrides from chrome config into the preview config.
39
66
  // Token overrides (from Token Tuner) are stored separately from configurator
40
67
  // state, but should apply to the preview so components render accurately.
@@ -51,9 +78,6 @@ export function Editor({
51
78
  <EditorShell
52
79
  sidebar={
53
80
  <Sidebar
54
- state={editor.configuratorState}
55
- dispatch={editor.dispatch}
56
- previewColors={editor.previewColors}
57
81
  isDirty={editor.isDirty}
58
82
  onRevert={editor.handleRevert}
59
83
  presets={editor.presets}
@@ -64,8 +88,6 @@ export function Editor({
64
88
  onRenamePreset={editor.renamePreset}
65
89
  onDeletePreset={editor.deletePreset}
66
90
  onDuplicatePreset={editor.duplicatePreset}
67
- colorMode={editor.colorMode}
68
- onColorModeChange={editor.handleColorModeChange}
69
91
  />
70
92
  }
71
93
  navbar={
@@ -87,11 +109,28 @@ export function Editor({
87
109
  minWidth: 0,
88
110
  }}
89
111
  >
90
- <TableOfContents
91
- activeView={editor.previewView}
92
- selectedComponentId={editor.selectedComponentId}
93
- onNavigate={editor.handlePreviewNavigate}
112
+ <PrimaryNav
113
+ activeSectionId={editor.activeSectionId}
114
+ onSelectSection={editor.handleSectionChange}
94
115
  />
116
+ {editor.activeSectionId === "components" ? (
117
+ <TableOfContents
118
+ activeSectionId={editor.activeSectionId}
119
+ activeView={editor.previewView}
120
+ selectedComponentId={editor.selectedComponentId}
121
+ onNavigate={editor.handlePreviewNavigate}
122
+ />
123
+ ) : (
124
+ <ConfiguratorPanel
125
+ activeSectionId={editor.activeSectionId}
126
+ state={editor.configuratorState}
127
+ dispatch={editor.dispatch}
128
+ previewColors={editor.previewColors}
129
+ colorMode={editor.colorMode}
130
+ onColorModeChange={editor.handleColorModeChange}
131
+ fontCatalog={fontCatalog}
132
+ />
133
+ )}
95
134
  <div
96
135
  style={{
97
136
  flex: 1,
@@ -114,6 +153,10 @@ export function Editor({
114
153
  onSelectVariant={editor.handleSelectVariant}
115
154
  propOverrides={editor.propOverrides}
116
155
  onPropOverride={editor.handlePropOverride}
156
+ roleWeights={roleWeights}
157
+ onRoleWeightChange={handleRoleWeightChange}
158
+ fontCatalog={fontCatalog}
159
+ scopeFontMap={scopeFontMap}
117
160
  />
118
161
  </div>
119
162
  </NewtoneProvider>
@@ -0,0 +1,77 @@
1
+ import { useTokens } from "@newtonedev/components";
2
+ import type { ColorMode } from "@newtonedev/components";
3
+ import { srgbToHex } from "newtone";
4
+ import type { ColorResult } from "newtone";
5
+ import type { ConfiguratorState } from "@newtonedev/configurator";
6
+ import type { ConfiguratorAction } from "@newtonedev/configurator";
7
+ import type { GoogleFontEntry } from "@newtonedev/fonts";
8
+ import {
9
+ ColorsSection,
10
+ DynamicRangeSection,
11
+ IconsSection,
12
+ FontsSection,
13
+ OthersSection,
14
+ } from "./sections";
15
+
16
+ interface ConfiguratorPanelProps {
17
+ readonly activeSectionId: string;
18
+ readonly state: ConfiguratorState;
19
+ readonly dispatch: (action: ConfiguratorAction) => void;
20
+ readonly previewColors: readonly (readonly ColorResult[])[];
21
+ readonly colorMode: ColorMode;
22
+ readonly onColorModeChange: (mode: ColorMode) => void;
23
+ readonly fontCatalog?: readonly GoogleFontEntry[];
24
+ }
25
+
26
+ const PANEL_WIDTH = 280;
27
+
28
+ export function ConfiguratorPanel({
29
+ activeSectionId,
30
+ state,
31
+ dispatch,
32
+ previewColors,
33
+ colorMode,
34
+ onColorModeChange,
35
+ fontCatalog,
36
+ }: ConfiguratorPanelProps) {
37
+ const tokens = useTokens();
38
+ const borderColor = srgbToHex(tokens.border.srgb);
39
+
40
+ return (
41
+ <div
42
+ style={{
43
+ width: PANEL_WIDTH,
44
+ flexShrink: 0,
45
+ overflowY: "auto",
46
+ borderRight: `1px solid ${borderColor}`,
47
+ padding: 20,
48
+ backgroundColor: srgbToHex(tokens.background.srgb),
49
+ display: "flex",
50
+ flexDirection: "column",
51
+ gap: 24,
52
+ }}
53
+ >
54
+ {activeSectionId === "colors" && (
55
+ <>
56
+ <DynamicRangeSection state={state} dispatch={dispatch} />
57
+ <ColorsSection
58
+ state={state}
59
+ dispatch={dispatch}
60
+ previewColors={previewColors}
61
+ colorMode={colorMode}
62
+ onColorModeChange={onColorModeChange}
63
+ />
64
+ </>
65
+ )}
66
+ {activeSectionId === "typography" && (
67
+ <FontsSection state={state} dispatch={dispatch} fontCatalog={fontCatalog} />
68
+ )}
69
+ {activeSectionId === "symbols" && (
70
+ <IconsSection state={state} dispatch={dispatch} />
71
+ )}
72
+ {activeSectionId === "layout" && (
73
+ <OthersSection state={state} dispatch={dispatch} />
74
+ )}
75
+ </div>
76
+ );
77
+ }
@@ -1,27 +1,29 @@
1
1
  import { useState, useRef, useEffect, useMemo, useCallback } from "react";
2
2
  import { useTokens } from "@newtonedev/components";
3
- import {
4
- GOOGLE_FONTS,
5
- SYSTEM_FONTS,
6
- } from "@newtonedev/components";
7
- import type { GoogleFontEntry, SystemFontEntry, FontConfig } from "@newtonedev/components";
3
+ import type { FontConfig } from "@newtonedev/components";
4
+ import { SYSTEM_FONTS } from "@newtonedev/fonts";
5
+ import type { GoogleFontEntry, SystemFontEntry } from "@newtonedev/fonts";
8
6
  import { srgbToHex } from "newtone";
9
7
 
10
- type FontSlot = "default" | "display" | "mono";
8
+ type FontSlot = "default" | "display" | "mono" | "currency";
11
9
 
12
10
  interface FontPickerProps {
13
11
  readonly label: string;
14
12
  readonly slot: FontSlot;
15
13
  readonly currentFont: FontConfig;
16
14
  readonly onSelect: (font: FontConfig) => void;
15
+ readonly fontCatalog?: readonly GoogleFontEntry[];
17
16
  }
18
17
 
19
- /** Preload all curated Google Fonts for preview on first dropdown open. */
20
- let previewLoaded = false;
21
- function preloadFontsForPreview() {
22
- if (previewLoaded || typeof document === "undefined") return;
23
- previewLoaded = true;
24
- const families = GOOGLE_FONTS.map(
18
+ /** Preload curated Google Fonts for preview on first dropdown open. */
19
+ let previewLoadedKey = "";
20
+ function preloadFontsForPreview(catalog: readonly GoogleFontEntry[]) {
21
+ if (catalog.length === 0 || typeof document === "undefined") return;
22
+ // Re-preload if the catalog changes (e.g. new fonts curated)
23
+ const key = catalog.map((f) => f.family).join(",");
24
+ if (key === previewLoadedKey) return;
25
+ previewLoadedKey = key;
26
+ const families = catalog.map(
25
27
  (f) => `family=${f.family.replace(/ /g, "+")}:wght@400`,
26
28
  ).join("&");
27
29
  const url = `https://fonts.googleapis.com/css2?${families}&display=swap`;
@@ -60,18 +62,13 @@ const CATEGORY_ORDER: readonly string[] = [
60
62
  "monospace",
61
63
  "display",
62
64
  ];
63
- const MONO_CATEGORY_ORDER: readonly string[] = [
64
- "monospace",
65
- "sans-serif",
66
- "serif",
67
- "display",
68
- ];
69
65
 
70
66
  export function FontPicker({
71
67
  label,
72
68
  slot,
73
69
  currentFont,
74
70
  onSelect,
71
+ fontCatalog = [],
75
72
  }: FontPickerProps) {
76
73
  const tokens = useTokens();
77
74
  const [isOpen, setIsOpen] = useState(false);
@@ -103,35 +100,47 @@ export function FontPicker({
103
100
 
104
101
  useEffect(() => {
105
102
  if (isOpen) {
106
- preloadFontsForPreview();
103
+ preloadFontsForPreview(fontCatalog);
107
104
  requestAnimationFrame(() => searchInputRef.current?.focus());
108
105
  }
109
- }, [isOpen]);
110
-
111
- const categoryOrder = slot === "mono" ? MONO_CATEGORY_ORDER : CATEGORY_ORDER;
106
+ }, [isOpen, fontCatalog]);
112
107
 
113
108
  const filteredGoogleFonts = useMemo(() => {
114
109
  const query = search.toLowerCase().trim();
115
- const fonts = query
116
- ? GOOGLE_FONTS.filter((f) => f.family.toLowerCase().includes(query))
117
- : GOOGLE_FONTS;
110
+ let fonts: readonly GoogleFontEntry[] = query
111
+ ? fontCatalog.filter((f) => f.family.toLowerCase().includes(query))
112
+ : fontCatalog;
113
+
114
+ if (slot === "mono") {
115
+ fonts = fonts.filter((f) => f.category === "monospace");
116
+ } else if (slot === "currency") {
117
+ fonts = fonts.filter((f) => f.category === "monospace" || f.category === "sans-serif");
118
+ }
118
119
 
119
120
  const grouped: Record<string, GoogleFontEntry[]> = {};
120
- for (const cat of categoryOrder) {
121
+ for (const cat of CATEGORY_ORDER) {
121
122
  const inCategory = fonts.filter((f) => f.category === cat);
122
123
  if (inCategory.length > 0) {
123
124
  grouped[cat] = inCategory;
124
125
  }
125
126
  }
126
127
  return grouped;
127
- }, [search, categoryOrder]);
128
+ }, [search, slot, fontCatalog]);
128
129
 
129
130
  const filteredSystemFonts = useMemo(() => {
130
131
  const query = search.toLowerCase().trim();
131
- return query
132
+ let fonts = query
132
133
  ? SYSTEM_FONTS.filter((f) => f.family.toLowerCase().includes(query))
133
134
  : [...SYSTEM_FONTS];
134
- }, [search]);
135
+
136
+ if (slot === "mono") {
137
+ fonts = fonts.filter((f) => f.category === "monospace");
138
+ } else if (slot === "currency") {
139
+ fonts = fonts.filter((f) => f.category !== "serif");
140
+ }
141
+
142
+ return fonts;
143
+ }, [search, slot]);
135
144
 
136
145
  const handleSelect = useCallback(
137
146
  (font: FontConfig) => {
@@ -1,10 +1,11 @@
1
1
  import { useCallback } from "react";
2
2
  import { useTokens } from "@newtonedev/components";
3
3
  import { srgbToHex } from "newtone";
4
+ import type { TextRole } from "@newtonedev/fonts";
4
5
  import { OverviewView } from "../preview/OverviewView";
5
6
  import { CategoryView } from "../preview/CategoryView";
6
7
  import { ComponentDetailView } from "../preview/ComponentDetailView";
7
- import type { PreviewView } from "../types";
8
+ import type { PreviewView, EditorFontEntry } from "../types";
8
9
 
9
10
  interface PreviewWindowProps {
10
11
  readonly view: PreviewView;
@@ -13,6 +14,10 @@ interface PreviewWindowProps {
13
14
  readonly onSelectVariant: (variantId: string) => void;
14
15
  readonly propOverrides?: Record<string, unknown>;
15
16
  readonly onPropOverride?: (name: string, value: unknown) => void;
17
+ readonly roleWeights?: Partial<Record<TextRole, number>>;
18
+ readonly onRoleWeightChange?: (role: TextRole, weight: number) => void;
19
+ readonly fontCatalog?: readonly EditorFontEntry[];
20
+ readonly scopeFontMap?: Record<string, string>;
16
21
  }
17
22
 
18
23
  export function PreviewWindow({
@@ -22,6 +27,10 @@ export function PreviewWindow({
22
27
  onSelectVariant,
23
28
  propOverrides,
24
29
  onPropOverride,
30
+ roleWeights,
31
+ onRoleWeightChange,
32
+ fontCatalog,
33
+ scopeFontMap,
25
34
  }: PreviewWindowProps) {
26
35
  const tokens = useTokens();
27
36
 
@@ -64,6 +73,10 @@ export function PreviewWindow({
64
73
  onSelectVariant={onSelectVariant}
65
74
  propOverrides={propOverrides}
66
75
  onPropOverride={onPropOverride}
76
+ roleWeights={roleWeights}
77
+ onRoleWeightChange={onRoleWeightChange}
78
+ fontCatalog={fontCatalog}
79
+ scopeFontMap={scopeFontMap}
67
80
  />
68
81
  )}
69
82
  </div>
@@ -0,0 +1,76 @@
1
+ import { useState } from "react";
2
+ import { useTokens, Icon, CATEGORIES } from "@newtonedev/components";
3
+ import { srgbToHex } from "newtone";
4
+
5
+ interface PrimaryNavProps {
6
+ readonly activeSectionId: string | null;
7
+ readonly onSelectSection: (sectionId: string) => void;
8
+ }
9
+
10
+ const NAV_WIDTH = 60;
11
+
12
+ export function PrimaryNav({ activeSectionId, onSelectSection }: PrimaryNavProps) {
13
+ const tokens = useTokens();
14
+ const [hoveredId, setHoveredId] = useState<string | null>(null);
15
+
16
+ const bg = srgbToHex(tokens.background.srgb);
17
+ const borderColor = srgbToHex(tokens.border.srgb);
18
+ const activeBg = srgbToHex(tokens.backgroundInteractive.srgb);
19
+ const iconColor = srgbToHex(tokens.textSecondary.srgb);
20
+ const activeIconColor = srgbToHex(tokens.textPrimary.srgb);
21
+
22
+ return (
23
+ <nav
24
+ aria-label="Section navigation"
25
+ style={{
26
+ width: NAV_WIDTH,
27
+ flexShrink: 0,
28
+ display: "flex",
29
+ flexDirection: "column",
30
+ alignItems: "center",
31
+ paddingTop: 12,
32
+ gap: 4,
33
+ backgroundColor: bg,
34
+ borderRight: `1px solid ${borderColor}`,
35
+ }}
36
+ >
37
+ {CATEGORIES.map((category) => {
38
+ const isActive = activeSectionId === category.id;
39
+ const isHovered = hoveredId === category.id;
40
+
41
+ return (
42
+ <button
43
+ key={category.id}
44
+ onClick={() => onSelectSection(category.id)}
45
+ onMouseEnter={() => setHoveredId(category.id)}
46
+ onMouseLeave={() => setHoveredId(null)}
47
+ title={category.name}
48
+ aria-current={isActive ? "page" : undefined}
49
+ style={{
50
+ display: "flex",
51
+ alignItems: "center",
52
+ justifyContent: "center",
53
+ width: 44,
54
+ height: 44,
55
+ borderRadius: 12,
56
+ border: "none",
57
+ backgroundColor: isActive
58
+ ? activeBg
59
+ : isHovered
60
+ ? `${borderColor}20`
61
+ : "transparent",
62
+ cursor: "pointer",
63
+ transition: "background-color 150ms",
64
+ }}
65
+ >
66
+ <Icon
67
+ name={category.icon ?? "circle"}
68
+ size={24}
69
+ color={isActive ? activeIconColor : iconColor}
70
+ />
71
+ </button>
72
+ );
73
+ })}
74
+ </nav>
75
+ );
76
+ }