@deadragdoll/reactnu 0.1.76 → 0.1.78

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.
package/dist/index.cjs CHANGED
@@ -3177,6 +3177,7 @@ function writeStoredWindowSnapshot(windowStoreKey, snapshot) {
3177
3177
  } catch {
3178
3178
  }
3179
3179
  }
3180
+ var WINDOW_STORE_WRITE_DEBOUNCE_MS = 250;
3180
3181
  function resolveSavedWindowStyle(definition, mode, ownerCenteredStyle, index) {
3181
3182
  const snapshot = definition.onOpen?.() ?? readStoredWindowSnapshot(definition.windowStoreKey);
3182
3183
  const savedStyle = {};
@@ -3315,6 +3316,7 @@ function NuWindowProvider({
3315
3316
  const creationOrderRef = (0, import_react19.useRef)(0);
3316
3317
  const idRef = (0, import_react19.useRef)(0);
3317
3318
  const windowBoundsByIdRef = (0, import_react19.useRef)({});
3319
+ const pendingWindowStoreWritesRef = (0, import_react19.useRef)(/* @__PURE__ */ new Map());
3318
3320
  const [windows, setWindows] = (0, import_react19.useState)([]);
3319
3321
  const [windowBoundsById, setWindowBoundsById] = (0, import_react19.useState)({});
3320
3322
  const appHostMenuContext = (0, import_react19.useContext)(AppHostMenuContext);
@@ -3737,16 +3739,52 @@ function NuWindowProvider({
3737
3739
  onAppModalChange?.(hasAppModal);
3738
3740
  }, [hasAppModal, onAppModalChange]);
3739
3741
  (0, import_react19.useEffect)(() => {
3742
+ const pendingWindowStoreWrites = pendingWindowStoreWritesRef.current;
3743
+ const windowStoreKeyById = new Map(
3744
+ windows.map((windowEntry) => [windowEntry.id, windowEntry.windowStoreKey])
3745
+ );
3746
+ for (const [id, pending] of pendingWindowStoreWrites) {
3747
+ if (windowStoreKeyById.get(id) === pending.windowStoreKey) {
3748
+ continue;
3749
+ }
3750
+ clearTimeout(pending.timeoutId);
3751
+ pendingWindowStoreWrites.delete(id);
3752
+ writeStoredWindowSnapshot(pending.windowStoreKey, pending.snapshot);
3753
+ }
3740
3754
  for (const windowEntry of windows) {
3741
3755
  if (!windowEntry.windowStoreKey) {
3742
3756
  continue;
3743
3757
  }
3744
- writeStoredWindowSnapshot(
3745
- windowEntry.windowStoreKey,
3746
- resolveManagedWindowSnapshot(windowEntry, windowBoundsById)
3758
+ const existing = pendingWindowStoreWrites.get(windowEntry.id);
3759
+ if (existing) {
3760
+ clearTimeout(existing.timeoutId);
3761
+ }
3762
+ const windowStoreKey = windowEntry.windowStoreKey;
3763
+ const snapshot = resolveManagedWindowSnapshot(
3764
+ windowEntry,
3765
+ windowBoundsById
3747
3766
  );
3767
+ const timeoutId = setTimeout(() => {
3768
+ writeStoredWindowSnapshot(windowStoreKey, snapshot);
3769
+ pendingWindowStoreWrites.delete(windowEntry.id);
3770
+ }, WINDOW_STORE_WRITE_DEBOUNCE_MS);
3771
+ pendingWindowStoreWrites.set(windowEntry.id, {
3772
+ timeoutId,
3773
+ windowStoreKey,
3774
+ snapshot
3775
+ });
3748
3776
  }
3749
3777
  }, [windowBoundsById, windows]);
3778
+ (0, import_react19.useEffect)(() => {
3779
+ const pendingWindowStoreWrites = pendingWindowStoreWritesRef.current;
3780
+ return () => {
3781
+ for (const entry of pendingWindowStoreWrites.values()) {
3782
+ clearTimeout(entry.timeoutId);
3783
+ writeStoredWindowSnapshot(entry.windowStoreKey, entry.snapshot);
3784
+ }
3785
+ pendingWindowStoreWrites.clear();
3786
+ };
3787
+ }, []);
3750
3788
  return /* @__PURE__ */ (0, import_jsx_runtime29.jsx)(NuWindowContext.Provider, { value: contextValue, children: /* @__PURE__ */ (0, import_jsx_runtime29.jsxs)("div", { className: ["nu-window-host", className].filter(Boolean).join(" "), children: [
3751
3789
  children,
3752
3790
  /* @__PURE__ */ (0, import_jsx_runtime29.jsx)("div", { className: "nu-window-layer", children: renderWindows.map((windowEntry) => {
@@ -5046,7 +5084,7 @@ function NuIconGridItem({
5046
5084
  icon.contextMenuItems,
5047
5085
  icon
5048
5086
  );
5049
- const dragSource = useNuDragSource({
5087
+ const { dragRef, isDragging } = useNuDragSource({
5050
5088
  disabled: icon.disabled,
5051
5089
  getItem: () => ({ data: icon, id: icon.id, type: "icon" }),
5052
5090
  onDropAccepted: (result) => {
@@ -5089,14 +5127,14 @@ function NuIconGridItem({
5089
5127
  {
5090
5128
  "aria-haspopup": contextMenuItems.length > 0 ? "menu" : void 0,
5091
5129
  className: "nu-icon-grid__icon",
5092
- "data-dragging": dragSource.isDragging || void 0,
5130
+ "data-dragging": isDragging || void 0,
5093
5131
  "data-selected": manager.selectedIconId === icon.id || void 0,
5094
5132
  disabled: icon.disabled,
5095
5133
  onClick: handleClick,
5096
5134
  onContextMenu: handleContextMenu,
5097
5135
  onDoubleClick: icon.onDoubleClick,
5098
5136
  onKeyDown: handleKeyDown,
5099
- ref: dragSource.dragRef,
5137
+ ref: (node) => dragRef(node),
5100
5138
  style: { left: icon.position.x, top: icon.position.y },
5101
5139
  type: "button",
5102
5140
  children: [
@@ -10539,6 +10577,9 @@ var phosphorTheme = {
10539
10577
  var midnightTheme = {
10540
10578
  name: "midnight",
10541
10579
  label: "Midnight Slate",
10580
+ desktopPatternMode: "coarse-dots",
10581
+ fontFamily: '"Courier New", Courier, monospace',
10582
+ fontSize: 16,
10542
10583
  tokens: {
10543
10584
  desktopBackground: "#1b2430",
10544
10585
  desktopPattern: "rgb(156 177 201 / 0.24)",
@@ -10548,10 +10589,10 @@ var midnightTheme = {
10548
10589
  chromeBackground: "#36547d",
10549
10590
  panelBackground: "#192c43",
10550
10591
  panelInsetBackground: "#111e2e",
10551
- titleBackground: "#0b2432",
10552
- menuBackground: "#1c2945",
10553
- titleText: "#c2d0e5",
10554
- textPrimary: "#6b9adb",
10592
+ titleBackground: "#1b3550",
10593
+ menuBackground: "#1a2337",
10594
+ titleText: "#a8cbff",
10595
+ textPrimary: "#b3d2ff",
10555
10596
  textMuted: "#65a8ec",
10556
10597
  textInverse: "#b7caf0",
10557
10598
  mainMenuText: "#b7caf0",
@@ -10560,7 +10601,7 @@ var midnightTheme = {
10560
10601
  buttonFace: "#5074af",
10561
10602
  buttonFaceAlt: "#3c5372",
10562
10603
  buttonDanger: "#4e0e15",
10563
- buttonSuccess: "#3c936d",
10604
+ buttonSuccess: "#275214",
10564
10605
  buttonText: "#ffffff",
10565
10606
  fieldBackground: "#09111c",
10566
10607
  fieldText: "#627b9d",
@@ -10572,6 +10613,66 @@ var midnightTheme = {
10572
10613
  focusColor: "#af532c",
10573
10614
  windowInactiveOverlay: "rgb(7 12 20 / 0.3)",
10574
10615
  windowModalBackdrop: "rgb(7 12 20 / 0.48)"
10616
+ },
10617
+ visualTokens: {
10618
+ "--nu-glyph-cell-size": "1em",
10619
+ "--nu-content-inset": "1em 1ch",
10620
+ "--nu-control-height": "1.2em",
10621
+ "--nu-space-xs": "0.25rem",
10622
+ "--nu-space-sm": "0.5rem",
10623
+ "--nu-space-md": "0.75rem",
10624
+ "--nu-space-lg": "1rem",
10625
+ "--nu-space-xl": "1.5rem",
10626
+ "--nu-radius-none": "0",
10627
+ "--nu-window-title-height": "calc(1em + 10px)",
10628
+ "--nu-window-status-height": "1.35rem",
10629
+ "--nu-window-title-controls-width": "7.8ch",
10630
+ "--nu-window-title-icon-width": "1em",
10631
+ "--nu-frame-thickness": "1px",
10632
+ "--nu-rule-thickness": "1px",
10633
+ "--nu-window-shadow-offset-x": "1ch",
10634
+ "--nu-window-shadow-offset-y": "1em",
10635
+ "--nu-button-shadow-x": "1ch",
10636
+ "--nu-button-shadow-y": "calc(var(--nu-control-height) / 2)",
10637
+ "--nu-button-shadow-offset-x": "var(--nu-button-shadow-x)",
10638
+ "--nu-button-shadow-offset-y": "calc(var(--nu-control-height) / 2)",
10639
+ "--nu-button-press-depth-x": "0.18em",
10640
+ "--nu-button-press-depth-y": "0.1em",
10641
+ "--nu-app-bar-height": "2.1rem",
10642
+ "--nu-app-bar-padding": "0.2rem 0.35rem",
10643
+ "--nu-toolbar-background": "#1b3550",
10644
+ "--nu-toolbar-inset": "1ch",
10645
+ "--nu-toolbar-border-color": "var(--nu-border-dark)",
10646
+ "--nu-toolbar-border-style": "none",
10647
+ "--nu-toolbar-border-width": "var(--nu-rule-thickness)",
10648
+ "--nu-toolbar-separator-color": "#6184bd",
10649
+ "--nu-toolbar-separator-style": "solid",
10650
+ "--nu-toolbar-separator-width": "var(--nu-rule-thickness)",
10651
+ "--nu-menu-divider-color": "#3d5a8a",
10652
+ "--nu-menu-divider-style": "solid",
10653
+ "--nu-menu-divider-width": "var(--nu-rule-thickness)",
10654
+ "--nu-window-shadow-bg": "rgb(0 0 0 / 0.38)",
10655
+ "--nu-window-inactive-border": "color-mix(in srgb, var(--nu-border-light) 78%, var(--nu-border-dark))",
10656
+ "--nu-scrollbar-track": "transparent",
10657
+ "--nu-scrollbar-thumb": "var(--nu-color-button-face)",
10658
+ "--nu-scrollbar-button-bg": "var(--nu-color-button-face)",
10659
+ "--nu-scrollbar-button-size": "0.875rem",
10660
+ "--nu-icon-grid-icon-background": "transparent",
10661
+ "--nu-icon-grid-icon-border-color": "transparent",
10662
+ "--nu-icon-grid-icon-border-style": "solid",
10663
+ "--nu-icon-grid-icon-opacity": "1",
10664
+ "--nu-icon-grid-icon-selected-background": "var(--nu-color-title-bg)",
10665
+ "--nu-icon-grid-icon-inactive-selected-background": "color-mix(in srgb, var(--nu-icon-grid-icon-selected-background) 65%, var(--nu-color-app-bg-alt))",
10666
+ "--nu-icon-grid-icon-selected-border-color": "transparent",
10667
+ "--nu-icon-grid-icon-selected-border-style": "none",
10668
+ "--nu-icon-grid-icon-selected-opacity": "1",
10669
+ "--nu-icon-grid-icon-focus-background": "#153051",
10670
+ "--nu-icon-grid-icon-focus-border-color": "#5672bd",
10671
+ "--nu-icon-grid-icon-focus-border-style": "solid",
10672
+ "--nu-icon-grid-icon-focus-opacity": "1",
10673
+ "--nu-icon-grid-icon-disabled-opacity": "0.6",
10674
+ "--nu-icon-grid-icon-drag-opacity": "0.35",
10675
+ "--nu-icon-grid-icon-drag-preview-opacity": "0.85"
10575
10676
  }
10576
10677
  };
10577
10678
  var grayscaleTheme = {
@@ -10660,7 +10761,8 @@ function getNuThemeStyle(theme) {
10660
10761
  "--nu-panel-shadow-color": theme.tokens.panelShadowColor,
10661
10762
  "--nu-focus-color": theme.tokens.focusColor ?? theme.tokens.borderAccent,
10662
10763
  "--nu-window-inactive-overlay": theme.tokens.windowInactiveOverlay,
10663
- "--nu-window-modal-backdrop": theme.tokens.windowModalBackdrop
10764
+ "--nu-window-modal-backdrop": theme.tokens.windowModalBackdrop,
10765
+ ...theme.visualTokens
10664
10766
  };
10665
10767
  }
10666
10768
  function getNuDesktopPatternStyle(mode) {
@@ -10716,9 +10818,9 @@ function NuThemeProvider({
10716
10818
  children,
10717
10819
  className,
10718
10820
  crtGlitch = false,
10719
- defaultDesktopPatternMode = "dot-grid",
10720
- defaultFontFamily = '"IBM Plex Mono", "Cascadia Mono", "Consolas", monospace',
10721
- defaultFontSize = 16,
10821
+ defaultDesktopPatternMode,
10822
+ defaultFontFamily,
10823
+ defaultFontSize,
10722
10824
  defaultTheme = "classic",
10723
10825
  desktopPatternMode,
10724
10826
  fontFamily,
@@ -10731,10 +10833,17 @@ function NuThemeProvider({
10731
10833
  theme
10732
10834
  }) {
10733
10835
  const generatedId = (0, import_react49.useId)();
10836
+ const defaultThemeDefinition = resolveNuTheme(theme ?? defaultTheme);
10734
10837
  const [internalTheme, setInternalTheme] = (0, import_react49.useState)(defaultTheme);
10735
- const [internalDesktopPatternMode, setInternalDesktopPatternMode] = (0, import_react49.useState)(defaultDesktopPatternMode);
10736
- const [internalFontFamily, setInternalFontFamily] = (0, import_react49.useState)(defaultFontFamily);
10737
- const [internalFontSize, setInternalFontSize] = (0, import_react49.useState)(defaultFontSize);
10838
+ const [internalDesktopPatternMode, setInternalDesktopPatternMode] = (0, import_react49.useState)(
10839
+ () => defaultDesktopPatternMode ?? defaultThemeDefinition.desktopPatternMode ?? "dot-grid"
10840
+ );
10841
+ const [internalFontFamily, setInternalFontFamily] = (0, import_react49.useState)(
10842
+ () => defaultFontFamily ?? defaultThemeDefinition.fontFamily ?? '"IBM Plex Mono", "Cascadia Mono", "Consolas", monospace'
10843
+ );
10844
+ const [internalFontSize, setInternalFontSize] = (0, import_react49.useState)(
10845
+ () => defaultFontSize ?? defaultThemeDefinition.fontSize ?? 16
10846
+ );
10738
10847
  const currentTheme = theme ?? internalTheme;
10739
10848
  const resolvedDesktopPatternMode = desktopPatternMode ?? internalDesktopPatternMode;
10740
10849
  const resolvedFontFamily = fontFamily ?? internalFontFamily;
package/dist/index.js CHANGED
@@ -3125,6 +3125,7 @@ function writeStoredWindowSnapshot(windowStoreKey, snapshot) {
3125
3125
  } catch {
3126
3126
  }
3127
3127
  }
3128
+ var WINDOW_STORE_WRITE_DEBOUNCE_MS = 250;
3128
3129
  function resolveSavedWindowStyle(definition, mode, ownerCenteredStyle, index) {
3129
3130
  const snapshot = definition.onOpen?.() ?? readStoredWindowSnapshot(definition.windowStoreKey);
3130
3131
  const savedStyle = {};
@@ -3263,6 +3264,7 @@ function NuWindowProvider({
3263
3264
  const creationOrderRef = useRef8(0);
3264
3265
  const idRef = useRef8(0);
3265
3266
  const windowBoundsByIdRef = useRef8({});
3267
+ const pendingWindowStoreWritesRef = useRef8(/* @__PURE__ */ new Map());
3266
3268
  const [windows, setWindows] = useState10([]);
3267
3269
  const [windowBoundsById, setWindowBoundsById] = useState10({});
3268
3270
  const appHostMenuContext = useContext7(AppHostMenuContext);
@@ -3685,16 +3687,52 @@ function NuWindowProvider({
3685
3687
  onAppModalChange?.(hasAppModal);
3686
3688
  }, [hasAppModal, onAppModalChange]);
3687
3689
  useEffect6(() => {
3690
+ const pendingWindowStoreWrites = pendingWindowStoreWritesRef.current;
3691
+ const windowStoreKeyById = new Map(
3692
+ windows.map((windowEntry) => [windowEntry.id, windowEntry.windowStoreKey])
3693
+ );
3694
+ for (const [id, pending] of pendingWindowStoreWrites) {
3695
+ if (windowStoreKeyById.get(id) === pending.windowStoreKey) {
3696
+ continue;
3697
+ }
3698
+ clearTimeout(pending.timeoutId);
3699
+ pendingWindowStoreWrites.delete(id);
3700
+ writeStoredWindowSnapshot(pending.windowStoreKey, pending.snapshot);
3701
+ }
3688
3702
  for (const windowEntry of windows) {
3689
3703
  if (!windowEntry.windowStoreKey) {
3690
3704
  continue;
3691
3705
  }
3692
- writeStoredWindowSnapshot(
3693
- windowEntry.windowStoreKey,
3694
- resolveManagedWindowSnapshot(windowEntry, windowBoundsById)
3706
+ const existing = pendingWindowStoreWrites.get(windowEntry.id);
3707
+ if (existing) {
3708
+ clearTimeout(existing.timeoutId);
3709
+ }
3710
+ const windowStoreKey = windowEntry.windowStoreKey;
3711
+ const snapshot = resolveManagedWindowSnapshot(
3712
+ windowEntry,
3713
+ windowBoundsById
3695
3714
  );
3715
+ const timeoutId = setTimeout(() => {
3716
+ writeStoredWindowSnapshot(windowStoreKey, snapshot);
3717
+ pendingWindowStoreWrites.delete(windowEntry.id);
3718
+ }, WINDOW_STORE_WRITE_DEBOUNCE_MS);
3719
+ pendingWindowStoreWrites.set(windowEntry.id, {
3720
+ timeoutId,
3721
+ windowStoreKey,
3722
+ snapshot
3723
+ });
3696
3724
  }
3697
3725
  }, [windowBoundsById, windows]);
3726
+ useEffect6(() => {
3727
+ const pendingWindowStoreWrites = pendingWindowStoreWritesRef.current;
3728
+ return () => {
3729
+ for (const entry of pendingWindowStoreWrites.values()) {
3730
+ clearTimeout(entry.timeoutId);
3731
+ writeStoredWindowSnapshot(entry.windowStoreKey, entry.snapshot);
3732
+ }
3733
+ pendingWindowStoreWrites.clear();
3734
+ };
3735
+ }, []);
3698
3736
  return /* @__PURE__ */ jsx29(NuWindowContext.Provider, { value: contextValue, children: /* @__PURE__ */ jsxs17("div", { className: ["nu-window-host", className].filter(Boolean).join(" "), children: [
3699
3737
  children,
3700
3738
  /* @__PURE__ */ jsx29("div", { className: "nu-window-layer", children: renderWindows.map((windowEntry) => {
@@ -5011,7 +5049,7 @@ function NuIconGridItem({
5011
5049
  icon.contextMenuItems,
5012
5050
  icon
5013
5051
  );
5014
- const dragSource = useNuDragSource({
5052
+ const { dragRef, isDragging } = useNuDragSource({
5015
5053
  disabled: icon.disabled,
5016
5054
  getItem: () => ({ data: icon, id: icon.id, type: "icon" }),
5017
5055
  onDropAccepted: (result) => {
@@ -5054,14 +5092,14 @@ function NuIconGridItem({
5054
5092
  {
5055
5093
  "aria-haspopup": contextMenuItems.length > 0 ? "menu" : void 0,
5056
5094
  className: "nu-icon-grid__icon",
5057
- "data-dragging": dragSource.isDragging || void 0,
5095
+ "data-dragging": isDragging || void 0,
5058
5096
  "data-selected": manager.selectedIconId === icon.id || void 0,
5059
5097
  disabled: icon.disabled,
5060
5098
  onClick: handleClick,
5061
5099
  onContextMenu: handleContextMenu,
5062
5100
  onDoubleClick: icon.onDoubleClick,
5063
5101
  onKeyDown: handleKeyDown,
5064
- ref: dragSource.dragRef,
5102
+ ref: (node) => dragRef(node),
5065
5103
  style: { left: icon.position.x, top: icon.position.y },
5066
5104
  type: "button",
5067
5105
  children: [
@@ -10588,6 +10626,9 @@ var phosphorTheme = {
10588
10626
  var midnightTheme = {
10589
10627
  name: "midnight",
10590
10628
  label: "Midnight Slate",
10629
+ desktopPatternMode: "coarse-dots",
10630
+ fontFamily: '"Courier New", Courier, monospace',
10631
+ fontSize: 16,
10591
10632
  tokens: {
10592
10633
  desktopBackground: "#1b2430",
10593
10634
  desktopPattern: "rgb(156 177 201 / 0.24)",
@@ -10597,10 +10638,10 @@ var midnightTheme = {
10597
10638
  chromeBackground: "#36547d",
10598
10639
  panelBackground: "#192c43",
10599
10640
  panelInsetBackground: "#111e2e",
10600
- titleBackground: "#0b2432",
10601
- menuBackground: "#1c2945",
10602
- titleText: "#c2d0e5",
10603
- textPrimary: "#6b9adb",
10641
+ titleBackground: "#1b3550",
10642
+ menuBackground: "#1a2337",
10643
+ titleText: "#a8cbff",
10644
+ textPrimary: "#b3d2ff",
10604
10645
  textMuted: "#65a8ec",
10605
10646
  textInverse: "#b7caf0",
10606
10647
  mainMenuText: "#b7caf0",
@@ -10609,7 +10650,7 @@ var midnightTheme = {
10609
10650
  buttonFace: "#5074af",
10610
10651
  buttonFaceAlt: "#3c5372",
10611
10652
  buttonDanger: "#4e0e15",
10612
- buttonSuccess: "#3c936d",
10653
+ buttonSuccess: "#275214",
10613
10654
  buttonText: "#ffffff",
10614
10655
  fieldBackground: "#09111c",
10615
10656
  fieldText: "#627b9d",
@@ -10621,6 +10662,66 @@ var midnightTheme = {
10621
10662
  focusColor: "#af532c",
10622
10663
  windowInactiveOverlay: "rgb(7 12 20 / 0.3)",
10623
10664
  windowModalBackdrop: "rgb(7 12 20 / 0.48)"
10665
+ },
10666
+ visualTokens: {
10667
+ "--nu-glyph-cell-size": "1em",
10668
+ "--nu-content-inset": "1em 1ch",
10669
+ "--nu-control-height": "1.2em",
10670
+ "--nu-space-xs": "0.25rem",
10671
+ "--nu-space-sm": "0.5rem",
10672
+ "--nu-space-md": "0.75rem",
10673
+ "--nu-space-lg": "1rem",
10674
+ "--nu-space-xl": "1.5rem",
10675
+ "--nu-radius-none": "0",
10676
+ "--nu-window-title-height": "calc(1em + 10px)",
10677
+ "--nu-window-status-height": "1.35rem",
10678
+ "--nu-window-title-controls-width": "7.8ch",
10679
+ "--nu-window-title-icon-width": "1em",
10680
+ "--nu-frame-thickness": "1px",
10681
+ "--nu-rule-thickness": "1px",
10682
+ "--nu-window-shadow-offset-x": "1ch",
10683
+ "--nu-window-shadow-offset-y": "1em",
10684
+ "--nu-button-shadow-x": "1ch",
10685
+ "--nu-button-shadow-y": "calc(var(--nu-control-height) / 2)",
10686
+ "--nu-button-shadow-offset-x": "var(--nu-button-shadow-x)",
10687
+ "--nu-button-shadow-offset-y": "calc(var(--nu-control-height) / 2)",
10688
+ "--nu-button-press-depth-x": "0.18em",
10689
+ "--nu-button-press-depth-y": "0.1em",
10690
+ "--nu-app-bar-height": "2.1rem",
10691
+ "--nu-app-bar-padding": "0.2rem 0.35rem",
10692
+ "--nu-toolbar-background": "#1b3550",
10693
+ "--nu-toolbar-inset": "1ch",
10694
+ "--nu-toolbar-border-color": "var(--nu-border-dark)",
10695
+ "--nu-toolbar-border-style": "none",
10696
+ "--nu-toolbar-border-width": "var(--nu-rule-thickness)",
10697
+ "--nu-toolbar-separator-color": "#6184bd",
10698
+ "--nu-toolbar-separator-style": "solid",
10699
+ "--nu-toolbar-separator-width": "var(--nu-rule-thickness)",
10700
+ "--nu-menu-divider-color": "#3d5a8a",
10701
+ "--nu-menu-divider-style": "solid",
10702
+ "--nu-menu-divider-width": "var(--nu-rule-thickness)",
10703
+ "--nu-window-shadow-bg": "rgb(0 0 0 / 0.38)",
10704
+ "--nu-window-inactive-border": "color-mix(in srgb, var(--nu-border-light) 78%, var(--nu-border-dark))",
10705
+ "--nu-scrollbar-track": "transparent",
10706
+ "--nu-scrollbar-thumb": "var(--nu-color-button-face)",
10707
+ "--nu-scrollbar-button-bg": "var(--nu-color-button-face)",
10708
+ "--nu-scrollbar-button-size": "0.875rem",
10709
+ "--nu-icon-grid-icon-background": "transparent",
10710
+ "--nu-icon-grid-icon-border-color": "transparent",
10711
+ "--nu-icon-grid-icon-border-style": "solid",
10712
+ "--nu-icon-grid-icon-opacity": "1",
10713
+ "--nu-icon-grid-icon-selected-background": "var(--nu-color-title-bg)",
10714
+ "--nu-icon-grid-icon-inactive-selected-background": "color-mix(in srgb, var(--nu-icon-grid-icon-selected-background) 65%, var(--nu-color-app-bg-alt))",
10715
+ "--nu-icon-grid-icon-selected-border-color": "transparent",
10716
+ "--nu-icon-grid-icon-selected-border-style": "none",
10717
+ "--nu-icon-grid-icon-selected-opacity": "1",
10718
+ "--nu-icon-grid-icon-focus-background": "#153051",
10719
+ "--nu-icon-grid-icon-focus-border-color": "#5672bd",
10720
+ "--nu-icon-grid-icon-focus-border-style": "solid",
10721
+ "--nu-icon-grid-icon-focus-opacity": "1",
10722
+ "--nu-icon-grid-icon-disabled-opacity": "0.6",
10723
+ "--nu-icon-grid-icon-drag-opacity": "0.35",
10724
+ "--nu-icon-grid-icon-drag-preview-opacity": "0.85"
10624
10725
  }
10625
10726
  };
10626
10727
  var grayscaleTheme = {
@@ -10709,7 +10810,8 @@ function getNuThemeStyle(theme) {
10709
10810
  "--nu-panel-shadow-color": theme.tokens.panelShadowColor,
10710
10811
  "--nu-focus-color": theme.tokens.focusColor ?? theme.tokens.borderAccent,
10711
10812
  "--nu-window-inactive-overlay": theme.tokens.windowInactiveOverlay,
10712
- "--nu-window-modal-backdrop": theme.tokens.windowModalBackdrop
10813
+ "--nu-window-modal-backdrop": theme.tokens.windowModalBackdrop,
10814
+ ...theme.visualTokens
10713
10815
  };
10714
10816
  }
10715
10817
  function getNuDesktopPatternStyle(mode) {
@@ -10765,9 +10867,9 @@ function NuThemeProvider({
10765
10867
  children,
10766
10868
  className,
10767
10869
  crtGlitch = false,
10768
- defaultDesktopPatternMode = "dot-grid",
10769
- defaultFontFamily = '"IBM Plex Mono", "Cascadia Mono", "Consolas", monospace',
10770
- defaultFontSize = 16,
10870
+ defaultDesktopPatternMode,
10871
+ defaultFontFamily,
10872
+ defaultFontSize,
10771
10873
  defaultTheme = "classic",
10772
10874
  desktopPatternMode,
10773
10875
  fontFamily,
@@ -10780,10 +10882,17 @@ function NuThemeProvider({
10780
10882
  theme
10781
10883
  }) {
10782
10884
  const generatedId = useId18();
10885
+ const defaultThemeDefinition = resolveNuTheme(theme ?? defaultTheme);
10783
10886
  const [internalTheme, setInternalTheme] = useState32(defaultTheme);
10784
- const [internalDesktopPatternMode, setInternalDesktopPatternMode] = useState32(defaultDesktopPatternMode);
10785
- const [internalFontFamily, setInternalFontFamily] = useState32(defaultFontFamily);
10786
- const [internalFontSize, setInternalFontSize] = useState32(defaultFontSize);
10887
+ const [internalDesktopPatternMode, setInternalDesktopPatternMode] = useState32(
10888
+ () => defaultDesktopPatternMode ?? defaultThemeDefinition.desktopPatternMode ?? "dot-grid"
10889
+ );
10890
+ const [internalFontFamily, setInternalFontFamily] = useState32(
10891
+ () => defaultFontFamily ?? defaultThemeDefinition.fontFamily ?? '"IBM Plex Mono", "Cascadia Mono", "Consolas", monospace'
10892
+ );
10893
+ const [internalFontSize, setInternalFontSize] = useState32(
10894
+ () => defaultFontSize ?? defaultThemeDefinition.fontSize ?? 16
10895
+ );
10787
10896
  const currentTheme = theme ?? internalTheme;
10788
10897
  const resolvedDesktopPatternMode = desktopPatternMode ?? internalDesktopPatternMode;
10789
10898
  const resolvedFontFamily = fontFamily ?? internalFontFamily;
@@ -34,10 +34,15 @@ export type NuThemeTokens = {
34
34
  windowInactiveOverlay: string;
35
35
  windowModalBackdrop: string;
36
36
  };
37
+ export type NuThemeVisualTokens = Record<`--nu-${string}`, string>;
37
38
  export type NuThemeDefinition = {
39
+ desktopPatternMode?: NuDesktopPatternMode;
40
+ fontFamily?: string;
41
+ fontSize?: number;
38
42
  name: string;
39
43
  label: string;
40
44
  tokens: NuThemeTokens;
45
+ visualTokens?: NuThemeVisualTokens;
41
46
  };
42
47
  export declare const nuThemes: {
43
48
  readonly classic: NuThemeDefinition;
@@ -63,7 +63,11 @@ Supported props:
63
63
 
64
64
  - Built-in themes are `classic` (default), `amber`, `phosphor`, `midnight`, and
65
65
  `grayscale`.
66
- - `midnight` is a dark slate-blue palette with blue chrome and a burnt-orange focus accent.
66
+ - Built-in themes may also define default desktop pattern, typography, and
67
+ visual CSS-token overrides. Explicit provider props still take precedence.
68
+ - `midnight` is a dark slate-blue palette with blue chrome, a burnt-orange
69
+ focus accent, coarse dots, and dedicated window, toolbar, menu, and icon-grid
70
+ chrome tokens.
67
71
  - `grayscale` is a high-contrast monochrome monitor palette. Semantic button variants use brightness rather than hue.
68
72
  - Theme tokens drive desktop, windowing, controls, selection, and inactive overlays.
69
73
  - Provider typography affects preview content, managed windows, dialogs, menus, and popup portals rendered inside the theme scope.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deadragdoll/reactnu",
3
- "version": "0.1.76",
3
+ "version": "0.1.78",
4
4
  "private": false,
5
5
  "description": "React components with a precise DOS-era utility interface.",
6
6
  "license": "MIT",