@newtonedev/editor 0.1.5 → 0.1.7

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 (62) hide show
  1. package/dist/Editor.d.ts +1 -1
  2. package/dist/Editor.d.ts.map +1 -1
  3. package/dist/components/CodeBlock.d.ts.map +1 -1
  4. package/dist/components/ConfiguratorPanel.d.ts +17 -0
  5. package/dist/components/ConfiguratorPanel.d.ts.map +1 -0
  6. package/dist/components/FontPicker.d.ts +4 -2
  7. package/dist/components/FontPicker.d.ts.map +1 -1
  8. package/dist/components/PresetSelector.d.ts.map +1 -1
  9. package/dist/components/PreviewWindow.d.ts +9 -3
  10. package/dist/components/PreviewWindow.d.ts.map +1 -1
  11. package/dist/components/PrimaryNav.d.ts +7 -0
  12. package/dist/components/PrimaryNav.d.ts.map +1 -0
  13. package/dist/components/RightSidebar.d.ts +4 -1
  14. package/dist/components/RightSidebar.d.ts.map +1 -1
  15. package/dist/components/Sidebar.d.ts +1 -10
  16. package/dist/components/Sidebar.d.ts.map +1 -1
  17. package/dist/components/TableOfContents.d.ts +2 -1
  18. package/dist/components/TableOfContents.d.ts.map +1 -1
  19. package/dist/components/sections/DynamicRangeSection.d.ts.map +1 -1
  20. package/dist/components/sections/FontsSection.d.ts +3 -1
  21. package/dist/components/sections/FontsSection.d.ts.map +1 -1
  22. package/dist/hooks/useEditorState.d.ts +4 -1
  23. package/dist/hooks/useEditorState.d.ts.map +1 -1
  24. package/dist/index.cjs +2893 -2248
  25. package/dist/index.cjs.map +1 -1
  26. package/dist/index.d.ts +2 -1
  27. package/dist/index.d.ts.map +1 -1
  28. package/dist/index.js +2895 -2251
  29. package/dist/index.js.map +1 -1
  30. package/dist/preview/ComponentDetailView.d.ts +9 -2
  31. package/dist/preview/ComponentDetailView.d.ts.map +1 -1
  32. package/dist/preview/ComponentRenderer.d.ts +2 -1
  33. package/dist/preview/ComponentRenderer.d.ts.map +1 -1
  34. package/dist/preview/IconBrowserView.d.ts +7 -0
  35. package/dist/preview/IconBrowserView.d.ts.map +1 -0
  36. package/dist/types.d.ts +17 -0
  37. package/dist/types.d.ts.map +1 -1
  38. package/dist/utils/lookupFontMetrics.d.ts +19 -0
  39. package/dist/utils/lookupFontMetrics.d.ts.map +1 -0
  40. package/dist/utils/measureFonts.d.ts +18 -0
  41. package/dist/utils/measureFonts.d.ts.map +1 -0
  42. package/package.json +1 -1
  43. package/src/Editor.tsx +57 -11
  44. package/src/components/CodeBlock.tsx +42 -14
  45. package/src/components/ConfiguratorPanel.tsx +77 -0
  46. package/src/components/FontPicker.tsx +38 -29
  47. package/src/components/PresetSelector.tsx +8 -33
  48. package/src/components/PreviewWindow.tsx +20 -4
  49. package/src/components/PrimaryNav.tsx +76 -0
  50. package/src/components/RightSidebar.tsx +103 -40
  51. package/src/components/Sidebar.tsx +4 -211
  52. package/src/components/TableOfContents.tsx +41 -78
  53. package/src/components/sections/DynamicRangeSection.tsx +2 -225
  54. package/src/components/sections/FontsSection.tsx +61 -93
  55. package/src/hooks/useEditorState.ts +68 -9
  56. package/src/index.ts +2 -0
  57. package/src/preview/ComponentDetailView.tsx +576 -73
  58. package/src/preview/ComponentRenderer.tsx +6 -4
  59. package/src/preview/IconBrowserView.tsx +187 -0
  60. package/src/types.ts +15 -0
  61. package/src/utils/lookupFontMetrics.ts +52 -0
  62. package/src/utils/measureFonts.ts +41 -0
@@ -1,9 +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
- readonly propOverrides?: Record<string, unknown>;
5
6
  readonly onSelectVariant: (variantId: string) => void;
7
+ readonly propOverrides?: Record<string, unknown>;
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>;
6
13
  }
7
- export declare function ComponentDetailView({ componentId, selectedVariantId, propOverrides, onSelectVariant, }: 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;
8
15
  export {};
9
16
  //# sourceMappingURL=ComponentDetailView.d.ts.map
@@ -1 +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"}
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"}
@@ -0,0 +1,7 @@
1
+ interface IconBrowserViewProps {
2
+ readonly selectedIconName: string;
3
+ readonly onIconSelect: (name: string) => void;
4
+ }
5
+ export declare function IconBrowserView({ selectedIconName, onIconSelect, }: IconBrowserViewProps): import("react/jsx-runtime").JSX.Element;
6
+ export {};
7
+ //# sourceMappingURL=IconBrowserView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"IconBrowserView.d.ts","sourceRoot":"","sources":["../../src/preview/IconBrowserView.tsx"],"names":[],"mappings":"AAIA,UAAU,oBAAoB;IAC5B,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;CAC/C;AAED,wBAAgB,eAAe,CAAC,EAC9B,gBAAgB,EAChB,YAAY,GACb,EAAE,oBAAoB,2CA8KtB"}
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":"AAAA,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAClE,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AACjE,OAAO,KAAK,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAC7E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEvC,uEAAuE;AACvE,MAAM,WAAW,eAAgB,SAAQ,eAAe;IACtD,QAAQ,CAAC,UAAU,CAAC,EAAE,OAAO,CAAC;IAC9B,QAAQ,CAAC,gBAAgB,CAAC,EAAE,SAAS,MAAM,EAAE,CAAC;IAC9C,8EAA8E;IAC9E,QAAQ,CAAC,eAAe,CAAC,EAAE;QAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC;CAC3E;AAID,MAAM,WAAW,MAAM;IACrB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,WAAW,EAAE,iBAAiB,CAAC;IACxC,QAAQ,CAAC,eAAe,EAAE,iBAAiB,GAAG,IAAI,CAAC;CACpD;AAID,MAAM,MAAM,UAAU,GAAG,OAAO,GAAG,QAAQ,GAAG,SAAS,GAAG,OAAO,CAAC;AAElE,MAAM,MAAM,WAAW,GACnB;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;CAAE,GAC7B;IAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAAE,GAC1D;IAAE,QAAQ,CAAC,IAAI,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjE,MAAM,MAAM,gBAAgB,GACxB,IAAI,GACJ;IAAE,QAAQ,CAAC,KAAK,EAAE,WAAW,CAAC;IAAC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAA;CAAE,GAC7D;IACE,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B,CAAC;AAIN,MAAM,WAAW,iBAAiB;IAChC,+CAA+C;IAC/C,QAAQ,CAAC,WAAW,EAAE,CAAC,MAAM,EAAE;QAC7B,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC;QAClC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;KACrC,KAAK,OAAO,CAAC;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAEnC,iCAAiC;IACjC,QAAQ,CAAC,SAAS,EAAE,CAAC,MAAM,EAAE;QAC3B,QAAQ,CAAC,KAAK,EAAE,iBAAiB,CAAC;QAClC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;QACpC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;QAChC,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC/C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;KAC3D,KAAK,OAAO,CAAC;QAAE,KAAK,CAAC,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAEnC,gEAAgE;IAChE,QAAQ,CAAC,cAAc,EAAE,CAAC,MAAM,EAAE;QAChC,QAAQ,CAAC,OAAO,EAAE,SAAS,MAAM,EAAE,CAAC;QACpC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;QAChC,QAAQ,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;KAC3C,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrB;AAID,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,CAAC,EAAE,SAAS,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,SAAS,CAAC;CAC5B;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,YAAY,EAAE,iBAAiB,CAAC;IACzC,QAAQ,CAAC,kBAAkB,EAAE,OAAO,CAAC;IACrC,QAAQ,CAAC,cAAc,EAAE,SAAS,MAAM,EAAE,CAAC;IAC3C,QAAQ,CAAC,qBAAqB,EAAE,MAAM,CAAC;IACvC,QAAQ,CAAC,wBAAwB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjD,QAAQ,CAAC,YAAY,EAAE,iBAAiB,CAAC;IACzC,QAAQ,CAAC,iBAAiB,EAAE,kBAAkB,CAAC;IAC/C,QAAQ,CAAC,WAAW,EAAE,iBAAiB,CAAC;IACxC,QAAQ,CAAC,WAAW,CAAC,EAAE,iBAAiB,CAAC;IACzC,QAAQ,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;IAClD,QAAQ,CAAC,kBAAkB,CAAC,EAAE,WAAW,CAAC;IAC1C,mEAAmE;IACnE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,iFAAiF;IACjF,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,eAAe,EAAE,CAAC;CACnD"}
@@ -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.5",
3
+ "version": "0.1.7",
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,
@@ -110,9 +149,14 @@ export function Editor({
110
149
  <PreviewWindow
111
150
  view={editor.previewView}
112
151
  selectedVariantId={editor.selectedVariantId}
113
- propOverrides={editor.propOverrides}
114
152
  onNavigate={editor.handlePreviewNavigate}
115
153
  onSelectVariant={editor.handleSelectVariant}
154
+ propOverrides={editor.propOverrides}
155
+ onPropOverride={editor.handlePropOverride}
156
+ roleWeights={roleWeights}
157
+ onRoleWeightChange={handleRoleWeightChange}
158
+ fontCatalog={fontCatalog}
159
+ scopeFontMap={scopeFontMap}
116
160
  />
117
161
  </div>
118
162
  </NewtoneProvider>
@@ -127,6 +171,8 @@ export function Editor({
127
171
  onResetOverrides={editor.handleResetOverrides}
128
172
  onClose={editor.handleCloseSidebar}
129
173
  onScopeToComponent={editor.handleScopeToComponent}
174
+ previewConfig={previewConfig}
175
+ colorMode={editor.colorMode}
130
176
  />
131
177
  }
132
178
  />
@@ -1,8 +1,9 @@
1
1
  import { useState, useCallback } from "react";
2
- import { useTokens, Button } from "@newtonedev/components";
2
+ import { useTokens, Icon } from "@newtonedev/components";
3
3
  import { srgbToHex } from "newtone";
4
4
 
5
5
  export function CopyButton({ text }: { readonly text: string }) {
6
+ const tokens = useTokens();
6
7
  const [copied, setCopied] = useState(false);
7
8
 
8
9
  const handleCopy = useCallback(async () => {
@@ -12,9 +13,31 @@ export function CopyButton({ text }: { readonly text: string }) {
12
13
  }, [text]);
13
14
 
14
15
  return (
15
- <Button variant="tertiary" semantic="neutral" size="sm" icon={copied ? "check" : "content_copy"} onPress={handleCopy}>
16
- {copied ? "Copied!" : "Copy"}
17
- </Button>
16
+ <button
17
+ onClick={handleCopy}
18
+ aria-label={copied ? "Copied" : "Copy code"}
19
+ style={{
20
+ background: "none",
21
+ border: "none",
22
+ cursor: "pointer",
23
+ padding: 4,
24
+ display: "flex",
25
+ alignItems: "center",
26
+ justifyContent: "center",
27
+ color: srgbToHex(
28
+ copied ? tokens.accent.fill.srgb : tokens.textTertiary.srgb,
29
+ ),
30
+ transition: "color 150ms ease",
31
+ }}
32
+ >
33
+ <Icon
34
+ name={copied ? "check" : "content_copy"}
35
+ size={16}
36
+ color={srgbToHex(
37
+ copied ? tokens.accent.fill.srgb : tokens.textTertiary.srgb,
38
+ )}
39
+ />
40
+ </button>
18
41
  );
19
42
  }
20
43
 
@@ -26,24 +49,29 @@ export function CodeBlock({
26
49
  const tokens = useTokens();
27
50
 
28
51
  return (
29
- <div style={{ position: "relative" }}>
52
+ <div
53
+ style={{
54
+ backgroundColor: srgbToHex(tokens.backgroundSunken.srgb),
55
+ border: `1px solid ${srgbToHex(tokens.border.srgb)}`,
56
+ borderRadius: 8,
57
+ overflow: "hidden",
58
+ }}
59
+ >
30
60
  <div
31
61
  style={{
32
- position: "absolute",
33
- top: 8,
34
- right: 8,
62
+ display: "flex",
63
+ justifyContent: "flex-end",
64
+ padding: "4px 8px",
65
+ borderBottom: `1px solid ${srgbToHex(tokens.border.srgb)}`,
35
66
  }}
36
67
  >
37
68
  <CopyButton text={code} />
38
69
  </div>
39
70
  <pre
40
71
  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",
72
+ padding: "12px 16px",
73
+ whiteSpace: "pre-wrap",
74
+ wordBreak: "break-word",
47
75
  fontSize: 13,
48
76
  lineHeight: 1.5,
49
77
  fontFamily: "'SF Mono', 'Fira Code', 'Fira Mono', Menlo, monospace",
@@ -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,5 +1,5 @@
1
1
  import { useState, useRef, useEffect, useCallback } from "react";
2
- import { useTokens } from "@newtonedev/components";
2
+ import { useTokens, Icon } from "@newtonedev/components";
3
3
  import { srgbToHex } from "newtone";
4
4
  import type { Preset } from "../types";
5
5
  import { presetHasUnpublishedChanges } from "../utils/presets";
@@ -145,21 +145,15 @@ export function PresetSelector({
145
145
  >
146
146
  {activePreset?.name ?? "Default"}
147
147
  </span>
148
- <svg
149
- width={10}
150
- height={10}
151
- viewBox="0 0 24 24"
152
- fill="none"
153
- stroke="currentColor"
154
- strokeWidth={2}
148
+ <Icon
149
+ name="expand_more"
150
+ size={14}
155
151
  style={{
156
152
  transform: isOpen ? "rotate(180deg)" : "none",
157
153
  transition: "transform 150ms ease",
158
154
  flexShrink: 0,
159
- }}
160
- >
161
- <polyline points="6 9 12 15 18 9" />
162
- </svg>
155
+ } as any}
156
+ />
163
157
  </button>
164
158
 
165
159
  {isOpen && (
@@ -303,16 +297,7 @@ export function PresetSelector({
303
297
  flexShrink: 0,
304
298
  }}
305
299
  >
306
- <svg
307
- width={14}
308
- height={14}
309
- viewBox="0 0 24 24"
310
- fill="currentColor"
311
- >
312
- <circle cx={12} cy={5} r={2} />
313
- <circle cx={12} cy={12} r={2} />
314
- <circle cx={12} cy={19} r={2} />
315
- </svg>
300
+ <Icon name="more_vert" size={14} color={textSecondary} />
316
301
  </button>
317
302
  )}
318
303
  </>
@@ -435,17 +420,7 @@ export function PresetSelector({
435
420
  cursor: "pointer",
436
421
  }}
437
422
  >
438
- <svg
439
- width={14}
440
- height={14}
441
- viewBox="0 0 24 24"
442
- fill="none"
443
- stroke="currentColor"
444
- strokeWidth={2}
445
- >
446
- <line x1={12} y1={5} x2={12} y2={19} />
447
- <line x1={5} y1={12} x2={19} y2={12} />
448
- </svg>
423
+ <Icon name="add" size={14} color={textSecondary} />
449
424
  New preset
450
425
  </button>
451
426
  </div>