@nanisoft/prism-ui 0.5.1 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (132) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/blocks/cta-01/cta.d.ts +66 -6
  3. package/dist/blocks/cta-01/cta.d.ts.map +1 -1
  4. package/dist/blocks/cta-01/cta.js +8 -3
  5. package/dist/blocks/cta-01/index.d.ts +1 -0
  6. package/dist/blocks/cta-01/index.d.ts.map +1 -1
  7. package/dist/blocks/feature-grid-01/feature-grid.d.ts +57 -6
  8. package/dist/blocks/feature-grid-01/feature-grid.d.ts.map +1 -1
  9. package/dist/blocks/feature-grid-01/feature-grid.js +12 -2
  10. package/dist/blocks/feature-grid-01/index.d.ts +1 -0
  11. package/dist/blocks/feature-grid-01/index.d.ts.map +1 -1
  12. package/dist/blocks/hero-01/hero.d.ts +30 -1
  13. package/dist/blocks/hero-01/hero.d.ts.map +1 -1
  14. package/dist/blocks/hero-01/hero.js +14 -2
  15. package/dist/blocks/index.d.ts +10 -0
  16. package/dist/blocks/index.d.ts.map +1 -1
  17. package/dist/blocks/index.js +9 -0
  18. package/dist/blocks/instrument-panel-01/index.d.ts +3 -0
  19. package/dist/blocks/instrument-panel-01/index.d.ts.map +1 -0
  20. package/dist/blocks/instrument-panel-01/index.js +1 -0
  21. package/dist/blocks/instrument-panel-01/instrument-panel.d.ts +114 -0
  22. package/dist/blocks/instrument-panel-01/instrument-panel.d.ts.map +1 -0
  23. package/dist/blocks/instrument-panel-01/instrument-panel.js +64 -0
  24. package/dist/blocks/logo-strip-01/index.d.ts +3 -0
  25. package/dist/blocks/logo-strip-01/index.d.ts.map +1 -0
  26. package/dist/blocks/logo-strip-01/index.js +1 -0
  27. package/dist/blocks/logo-strip-01/logo-strip.d.ts +84 -0
  28. package/dist/blocks/logo-strip-01/logo-strip.d.ts.map +1 -0
  29. package/dist/blocks/logo-strip-01/logo-strip.js +40 -0
  30. package/dist/blocks/note-grid-01/index.d.ts +3 -0
  31. package/dist/blocks/note-grid-01/index.d.ts.map +1 -0
  32. package/dist/blocks/note-grid-01/index.js +1 -0
  33. package/dist/blocks/note-grid-01/note-grid.d.ts +79 -0
  34. package/dist/blocks/note-grid-01/note-grid.d.ts.map +1 -0
  35. package/dist/blocks/note-grid-01/note-grid.js +37 -0
  36. package/dist/blocks/pricing-01/pricing.js +1 -1
  37. package/dist/blocks/process-rail-01/index.d.ts +3 -0
  38. package/dist/blocks/process-rail-01/index.d.ts.map +1 -0
  39. package/dist/blocks/process-rail-01/index.js +1 -0
  40. package/dist/blocks/process-rail-01/process-rail.d.ts +99 -0
  41. package/dist/blocks/process-rail-01/process-rail.d.ts.map +1 -0
  42. package/dist/blocks/process-rail-01/process-rail.js +58 -0
  43. package/dist/blocks/product-grid-01/index.d.ts +3 -0
  44. package/dist/blocks/product-grid-01/index.d.ts.map +1 -0
  45. package/dist/blocks/product-grid-01/index.js +1 -0
  46. package/dist/blocks/product-grid-01/product-grid.d.ts +114 -0
  47. package/dist/blocks/product-grid-01/product-grid.d.ts.map +1 -0
  48. package/dist/blocks/product-grid-01/product-grid.js +51 -0
  49. package/dist/blocks/site-footer/index.d.ts +3 -0
  50. package/dist/blocks/site-footer/index.d.ts.map +1 -0
  51. package/dist/blocks/site-footer/index.js +1 -0
  52. package/dist/blocks/site-footer/site-footer.d.ts +135 -0
  53. package/dist/blocks/site-footer/site-footer.d.ts.map +1 -0
  54. package/dist/blocks/site-footer/site-footer.js +41 -0
  55. package/dist/blocks/site-header/index.d.ts +3 -0
  56. package/dist/blocks/site-header/index.d.ts.map +1 -0
  57. package/dist/blocks/site-header/index.js +1 -0
  58. package/dist/blocks/site-header/site-header.d.ts +142 -0
  59. package/dist/blocks/site-header/site-header.d.ts.map +1 -0
  60. package/dist/blocks/site-header/site-header.js +46 -0
  61. package/dist/blocks/stack-grid-01/index.d.ts +3 -0
  62. package/dist/blocks/stack-grid-01/index.d.ts.map +1 -0
  63. package/dist/blocks/stack-grid-01/index.js +1 -0
  64. package/dist/blocks/stack-grid-01/stack-grid.d.ts +118 -0
  65. package/dist/blocks/stack-grid-01/stack-grid.d.ts.map +1 -0
  66. package/dist/blocks/stack-grid-01/stack-grid.js +49 -0
  67. package/dist/blocks/status-ledger-01/index.d.ts +3 -0
  68. package/dist/blocks/status-ledger-01/index.d.ts.map +1 -0
  69. package/dist/blocks/status-ledger-01/index.js +1 -0
  70. package/dist/blocks/status-ledger-01/status-ledger.d.ts +131 -0
  71. package/dist/blocks/status-ledger-01/status-ledger.d.ts.map +1 -0
  72. package/dist/blocks/status-ledger-01/status-ledger.js +80 -0
  73. package/dist/catalog.d.ts.map +1 -1
  74. package/dist/catalog.js +180 -0
  75. package/dist/components/index.d.ts +11 -0
  76. package/dist/components/index.d.ts.map +1 -1
  77. package/dist/components/index.js +6 -0
  78. package/dist/components/ui/card.d.ts +25 -0
  79. package/dist/components/ui/card.d.ts.map +1 -1
  80. package/dist/components/ui/card.js +25 -0
  81. package/dist/components/ui/cta-link.d.ts +45 -0
  82. package/dist/components/ui/cta-link.d.ts.map +1 -0
  83. package/dist/components/ui/cta-link.js +81 -0
  84. package/dist/components/ui/diagram.d.ts +154 -0
  85. package/dist/components/ui/diagram.d.ts.map +1 -0
  86. package/dist/components/ui/diagram.js +150 -0
  87. package/dist/components/ui/fact-list.d.ts +90 -0
  88. package/dist/components/ui/fact-list.d.ts.map +1 -0
  89. package/dist/components/ui/fact-list.js +38 -0
  90. package/dist/components/ui/product-mark.d.ts +115 -0
  91. package/dist/components/ui/product-mark.d.ts.map +1 -0
  92. package/dist/components/ui/product-mark.js +82 -0
  93. package/dist/components/ui/product-switcher.d.ts +108 -0
  94. package/dist/components/ui/product-switcher.d.ts.map +1 -0
  95. package/dist/components/ui/product-switcher.js +47 -0
  96. package/dist/components/ui/prose.d.ts +88 -0
  97. package/dist/components/ui/prose.d.ts.map +1 -0
  98. package/dist/components/ui/prose.js +53 -0
  99. package/dist/components/ui/section.d.ts +24 -1
  100. package/dist/components/ui/section.d.ts.map +1 -1
  101. package/dist/components/ui/section.js +11 -2
  102. package/dist/components/ui/slider.d.ts.map +1 -1
  103. package/dist/components/ui/slider.js +7 -1
  104. package/dist/index.d.ts +24 -0
  105. package/dist/index.d.ts.map +1 -1
  106. package/dist/index.js +17 -0
  107. package/dist/pages/blog-post-page/index.d.ts +174 -0
  108. package/dist/pages/blog-post-page/index.d.ts.map +1 -0
  109. package/dist/pages/blog-post-page/index.js +49 -0
  110. package/dist/pages/docs-shell/docs-shell.d.ts +250 -0
  111. package/dist/pages/docs-shell/docs-shell.d.ts.map +1 -0
  112. package/dist/pages/docs-shell/docs-shell.js +197 -0
  113. package/dist/pages/docs-shell/index.d.ts +3 -0
  114. package/dist/pages/docs-shell/index.d.ts.map +1 -0
  115. package/dist/pages/docs-shell/index.js +1 -0
  116. package/dist/pages/index.d.ts +13 -2
  117. package/dist/pages/index.d.ts.map +1 -1
  118. package/dist/pages/index.js +10 -2
  119. package/dist/pages/not-found-page/index.d.ts +112 -0
  120. package/dist/pages/not-found-page/index.d.ts.map +1 -0
  121. package/dist/pages/not-found-page/index.js +46 -0
  122. package/dist/provider/provider.d.ts +5 -0
  123. package/dist/provider/provider.d.ts.map +1 -1
  124. package/dist/provider/provider.js +129 -38
  125. package/dist/provider/theme-script.d.ts +41 -3
  126. package/dist/provider/theme-script.d.ts.map +1 -1
  127. package/dist/provider/theme-script.js +68 -17
  128. package/dist/styles.css +555 -12
  129. package/dist/theming/index.d.ts +132 -2
  130. package/dist/theming/index.d.ts.map +1 -1
  131. package/dist/theming/index.js +127 -1
  132. package/package.json +14 -4
@@ -9,67 +9,158 @@ import { jsx as _jsx } from "react/jsx-runtime";
9
9
  * the same two markup attributes the declarative form uses. There is no
10
10
  * override, merge, token, className or style prop: the provider's only outputs
11
11
  * are the document-element attributes and context (ticket 07).
12
+ *
13
+ * It resolves through `resolveTheme`, the one rule the boot script also
14
+ * implements, and it never writes `THEME_ORIGIN_ATTRIBUTE`: the script is that
15
+ * attribute's only writer, and two writers would turn a record of the origin
16
+ * into a race.
12
17
  */
13
- import { createContext, useCallback, useContext, useEffect, useMemo, useState, } from 'react';
14
- import { DEFAULT_STORAGE_KEY, MODES, PACKS, parseStoredTheme, themeAttributes, } from '../theming';
18
+ import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState, } from 'react';
19
+ import { DEFAULT_STORAGE_KEY, PACK_ATTRIBUTE, readLegacyMode, resolveTheme, themeAttributes, } from '../theming';
15
20
  const PrismThemeContext = createContext(null);
16
21
  function applyDocumentAttributes(pack, mode) {
17
22
  const root = document.documentElement;
18
23
  const { 'data-pack': packAttribute, className } = themeAttributes({ pack, mode });
19
24
  if (packAttribute)
20
- root.setAttribute('data-pack', packAttribute);
25
+ root.setAttribute(PACK_ATTRIBUTE, packAttribute);
21
26
  else
22
- root.removeAttribute('data-pack');
27
+ root.removeAttribute(PACK_ATTRIBUTE);
23
28
  if (className)
24
29
  root.classList.add('dark');
25
30
  else
26
31
  root.classList.remove('dark');
27
32
  }
28
- function packFromDocument(fallback) {
29
- const attribute = document.documentElement.getAttribute('data-pack');
30
- return attribute && PACKS.includes(attribute)
31
- ? attribute
32
- : fallback;
33
+ /** One guarded read. A blocked or full store must not break the page. */
34
+ function read(key) {
35
+ try {
36
+ return localStorage.getItem(key);
37
+ }
38
+ catch {
39
+ return null;
40
+ }
41
+ }
42
+ /** One guarded write. The attributes are already applied when this fails. */
43
+ function persist(key, pack, mode) {
44
+ try {
45
+ localStorage.setItem(key, JSON.stringify({ pack, mode }));
46
+ }
47
+ catch {
48
+ // A blocked or full localStorage must not break theme switching. The
49
+ // attributes are already applied, so the visible behaviour is unchanged.
50
+ }
33
51
  }
34
- function modeFromDocument(fallback) {
35
- return document.documentElement.classList.contains('dark') ? 'dark' : fallback;
52
+ function retire(key) {
53
+ try {
54
+ localStorage.removeItem(key);
55
+ }
56
+ catch {
57
+ // Nothing to do: a key that cannot be removed is read again next load,
58
+ // recovers the same decision, and writes the same pair.
59
+ }
36
60
  }
37
61
  export function PrismProvider({ defaultPack = 'default', defaultMode = 'light', storageKey = DEFAULT_STORAGE_KEY, children, }) {
38
- const [pack, setPackState] = useState(defaultPack);
39
- const [mode, setModeState] = useState(defaultMode);
40
- const [resolved, setResolved] = useState(false);
41
62
  /*
42
- * Resolution order on mount: stored value, then the server-rendered attribute,
43
- * then the default. A server-rendered pack therefore survives hydration
44
- * instead of flashing to the default.
63
+ * The resolution, and whether a decision has been made. Two pieces of state
64
+ * for two different questions, which is why the old `resolved` flag was not
65
+ * enough on its own:
66
+ *
67
+ * - `theme` is null until the store has been read. It gates APPLY, because
68
+ * applying the default over a server-rendered pack before resolution is
69
+ * the flash the resolution order exists to prevent. It carries the
70
+ * origin, so the recovery below can read it without a second lookup.
71
+ * - `decided` is a ref, not state, because it must be readable by the write
72
+ * effect without causing a render, and settable synchronously by a caller
73
+ * whose state update has not been committed yet. It gates WRITE.
74
+ *
75
+ * A ref also cannot be re-rendered into an extra effect pass, which is what
76
+ * would have made the write fire once for the flag and once for the value.
77
+ */
78
+ const [theme, setTheme] = useState(null);
79
+ const decided = useRef(false);
80
+ /*
81
+ * Resolution on mount, through the one shared rule: the stored value, then a
82
+ * key this line no longer writes, then what the server rendered, then the
83
+ * defaults. A server-rendered pack therefore survives hydration.
45
84
  */
46
85
  useEffect(() => {
47
- const stored = parseStoredTheme(localStorage.getItem(storageKey));
48
- const next = stored ?? { pack: packFromDocument(defaultPack), mode: modeFromDocument(defaultMode) };
49
- setPackState(next.pack);
50
- setModeState(next.mode);
51
- setResolved(true);
86
+ const recovered = readLegacyMode(read);
87
+ const next = resolveTheme({
88
+ stored: read(storageKey),
89
+ legacy: recovered?.mode ?? null,
90
+ documentPack: document.documentElement.getAttribute(PACK_ATTRIBUTE),
91
+ documentDark: document.documentElement.classList.contains('dark'),
92
+ defaultPack,
93
+ defaultMode,
94
+ });
95
+ if (next.origin === 'legacy' && recovered) {
96
+ /*
97
+ * The recovery write. A decision taken from a key this line no longer
98
+ * writes is put in the key it does write, and the old key is retired in
99
+ * the same breath, which is what makes the clause self-disabling: the
100
+ * next load finds nothing to recover, so a reader is migrated once and
101
+ * the population the clause can still reach only ever shrinks.
102
+ */
103
+ persist(storageKey, next.pack, next.mode);
104
+ retire(recovered.key);
105
+ }
106
+ // A caller that decided in a layout effect before this ran keeps its value.
107
+ if (!decided.current)
108
+ setTheme(next);
52
109
  }, [storageKey, defaultPack, defaultMode]);
53
110
  /*
54
- * Persist and apply only after resolution, so the first client commit never
55
- * writes the defaults over a stored or server-rendered choice.
111
+ * Apply always, once there is a resolution; persist only on a decision.
112
+ *
113
+ * The first resolution from the document or the defaults writes nothing, so a
114
+ * visitor who never touched the toggle ends the load with an empty store and
115
+ * a site that changes its default reaches everyone who has not chosen.
56
116
  */
57
117
  useEffect(() => {
58
- if (!resolved)
118
+ if (!theme)
59
119
  return;
60
- applyDocumentAttributes(pack, mode);
61
- try {
62
- localStorage.setItem(storageKey, JSON.stringify({ pack, mode }));
63
- }
64
- catch {
65
- // A blocked or full localStorage must not break theme switching. The
66
- // attributes are already applied, so the visible behaviour is unchanged.
67
- }
68
- }, [resolved, pack, mode, storageKey]);
69
- const setPack = useCallback((next) => setPackState(next), []);
70
- const setMode = useCallback((next) => setModeState(next), []);
71
- const toggleMode = useCallback(() => setModeState((current) => (current === 'dark' ? MODES[0] : MODES[1])), []);
72
- const value = useMemo(() => ({ pack, mode, setPack, setMode, toggleMode }), [pack, mode, setPack, setMode, toggleMode]);
120
+ applyDocumentAttributes(theme.pack, theme.mode);
121
+ if (decided.current)
122
+ persist(storageKey, theme.pack, theme.mode);
123
+ }, [theme, storageKey]);
124
+ const setPack = useCallback((next) => {
125
+ decided.current = true;
126
+ setTheme((current) => ({
127
+ pack: next,
128
+ mode: current?.mode ?? defaultMode,
129
+ origin: 'stored',
130
+ }));
131
+ }, [defaultMode]);
132
+ const setMode = useCallback((next) => {
133
+ decided.current = true;
134
+ setTheme((current) => ({
135
+ pack: current?.pack ?? defaultPack,
136
+ mode: next,
137
+ origin: 'stored',
138
+ }));
139
+ }, [defaultPack]);
140
+ /*
141
+ * The pair is a closed union of two members, so the other one is the other
142
+ * member. Spelling it out costs nothing and does not hide the coupling behind
143
+ * an index into a list this file does not otherwise need.
144
+ *
145
+ * The three setters all fall back to the consumer's own default rather than
146
+ * to the neutral one, so a decision taken before the store has been read
147
+ * pairs the chosen axis with the site's default and not with a guess.
148
+ */
149
+ const toggleMode = useCallback(() => {
150
+ decided.current = true;
151
+ setTheme((current) => ({
152
+ pack: current?.pack ?? defaultPack,
153
+ mode: current?.mode === 'dark' ? 'light' : 'dark',
154
+ origin: 'stored',
155
+ }));
156
+ }, [defaultPack]);
157
+ const value = useMemo(() => ({
158
+ pack: theme?.pack ?? defaultPack,
159
+ mode: theme?.mode ?? defaultMode,
160
+ setPack,
161
+ setMode,
162
+ toggleMode,
163
+ }), [theme, defaultPack, defaultMode, setPack, setMode, toggleMode]);
73
164
  return _jsx(PrismThemeContext.Provider, { value: value, children: children });
74
165
  }
75
166
  /**
@@ -3,11 +3,49 @@
3
3
  *
4
4
  * A Server Component that renders one blocking script. Placed in `<head>`, it
5
5
  * applies the stored pack and mode before React hydrates, so the first paint is
6
- * already correct. Precedence matches `PrismProvider`: stored value, then the
7
- * server-rendered attribute, then the defaults. It is safe to omit; a consumer
8
- * who renders the attributes on `<html>` needs no client runtime at all.
6
+ * already correct. It is safe to omit; a consumer who renders the attributes on
7
+ * `<html>` needs no client runtime at all.
8
+ *
9
+ * It is the only writer of `THEME_ORIGIN_ATTRIBUTE`, and it writes that
10
+ * attribute on every path it reaches, so the origin of the active theme is
11
+ * always readable and never merely absent.
12
+ *
13
+ * The string below is a second implementation of `resolveTheme`, not a copy of
14
+ * it that has been kept in step. A function cannot be shared with a string that
15
+ * a minifier rewrites: a renamed identifier inside the script's own `try` throws
16
+ * there, the catch swallows it, and the theme silently stops applying on every
17
+ * page. So the string implements the rule and
18
+ * `scripts/check-theme-resolution.mjs` runs BOTH over one table of stored
19
+ * values, which is the only thing that can hold them together.
20
+ *
21
+ * Its price is a separate ceiling, `scripts/check-boot-budget.mjs`, because this
22
+ * string runs before paint on every page and adding it to a table of component
23
+ * bundle sizes would corrupt the unit that table exists to measure.
9
24
  */
10
25
  import { type Mode, type PackId } from '../theming';
26
+ /**
27
+ * The first half of the storage-migration clause: read one key this line no
28
+ * longer writes.
29
+ *
30
+ * Exported because `check-boot-budget.mjs` prices the clause rather than
31
+ * guessing at it. The emitted string contains this fragment verbatim, so its
32
+ * byte count is the clause's cost and not an estimate of it.
33
+ */
34
+ export declare const THEME_MIGRATION_READ: string;
35
+ /**
36
+ * The second half: the clause retires itself.
37
+ *
38
+ * The recovery is the only write this string performs, and it writes the
39
+ * recovered pair into the key this line does write and removes the key it came
40
+ * from, in one guarded step. That is the clause's expiry by rule rather than by
41
+ * number: it fires only while a retired key still holds a mode, it fires at
42
+ * most once per reader because its own write is what makes the second load find
43
+ * nothing, and it is retired for good by deleting the last entry of
44
+ * `LEGACY_MODE_STORAGE_KEYS`. There is no date and no version constant in it,
45
+ * and the gate reports how many keys are still live so the number is visible
46
+ * rather than remembered.
47
+ */
48
+ export declare const THEME_MIGRATION_RETIRE = "if(G===\"legacy\"){try{localStorage.setItem(K,JSON.stringify({pack:pack,mode:m}));localStorage.removeItem(LK);}catch(e){}}";
11
49
  export type PrismThemeScriptProps = {
12
50
  storageKey?: string;
13
51
  defaultPack?: PackId;
@@ -1 +1 @@
1
- {"version":3,"file":"theme-script.d.ts","sourceRoot":"","sources":["../../src/provider/theme-script.tsx"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AACH,OAAO,EAAqC,KAAK,IAAI,EAAE,KAAK,MAAM,EAAE,MAAM,YAAY,CAAA;AAEtF,MAAM,MAAM,qBAAqB,GAAG;IAClC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,IAAI,CAAA;CACnB,CAAA;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,UAAgC,EAChC,WAAuB,EACvB,WAAqB,GACtB,EAAE,qBAAqB,+BAkBvB"}
1
+ {"version":3,"file":"theme-script.d.ts","sourceRoot":"","sources":["../../src/provider/theme-script.tsx"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,OAAO,EAOL,KAAK,IAAI,EACT,KAAK,MAAM,EACZ,MAAM,YAAY,CAAA;AAEnB;;;;;;;GAOG;AACH,eAAO,MAAM,oBAAoB,QAEvB,CAAA;AAEV;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,sBAAsB,+HACyF,CAAA;AAE5H,MAAM,MAAM,qBAAqB,GAAG;IAClC,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,WAAW,CAAC,EAAE,IAAI,CAAA;CACnB,CAAA;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,UAAgC,EAChC,WAAuB,EACvB,WAAqB,GACtB,EAAE,qBAAqB,+BA+BvB"}
@@ -4,25 +4,76 @@ import { jsx as _jsx } from "react/jsx-runtime";
4
4
  *
5
5
  * A Server Component that renders one blocking script. Placed in `<head>`, it
6
6
  * applies the stored pack and mode before React hydrates, so the first paint is
7
- * already correct. Precedence matches `PrismProvider`: stored value, then the
8
- * server-rendered attribute, then the defaults. It is safe to omit; a consumer
9
- * who renders the attributes on `<html>` needs no client runtime at all.
7
+ * already correct. It is safe to omit; a consumer who renders the attributes on
8
+ * `<html>` needs no client runtime at all.
9
+ *
10
+ * It is the only writer of `THEME_ORIGIN_ATTRIBUTE`, and it writes that
11
+ * attribute on every path it reaches, so the origin of the active theme is
12
+ * always readable and never merely absent.
13
+ *
14
+ * The string below is a second implementation of `resolveTheme`, not a copy of
15
+ * it that has been kept in step. A function cannot be shared with a string that
16
+ * a minifier rewrites: a renamed identifier inside the script's own `try` throws
17
+ * there, the catch swallows it, and the theme silently stops applying on every
18
+ * page. So the string implements the rule and
19
+ * `scripts/check-theme-resolution.mjs` runs BOTH over one table of stored
20
+ * values, which is the only thing that can hold them together.
21
+ *
22
+ * Its price is a separate ceiling, `scripts/check-boot-budget.mjs`, because this
23
+ * string runs before paint on every page and adding it to a table of component
24
+ * bundle sizes would corrupt the unit that table exists to measure.
25
+ */
26
+ import { DEFAULT_STORAGE_KEY, LEGACY_MODE_STORAGE_KEYS, MODES, PACKS, PACK_ATTRIBUTE, THEME_ORIGIN_ATTRIBUTE, } from '../theming';
27
+ /**
28
+ * The first half of the storage-migration clause: read one key this line no
29
+ * longer writes.
30
+ *
31
+ * Exported because `check-boot-budget.mjs` prices the clause rather than
32
+ * guessing at it. The emitted string contains this fragment verbatim, so its
33
+ * byte count is the clause's cost and not an estimate of it.
34
+ */
35
+ export const THEME_MIGRATION_READ = LEGACY_MODE_STORAGE_KEYS.map((key) => `try{var g=localStorage.getItem(${JSON.stringify(key)});if(M.indexOf(g)>-1){L=g;LK=${JSON.stringify(key)};}}catch(e){}`).join('');
36
+ /**
37
+ * The second half: the clause retires itself.
38
+ *
39
+ * The recovery is the only write this string performs, and it writes the
40
+ * recovered pair into the key this line does write and removes the key it came
41
+ * from, in one guarded step. That is the clause's expiry by rule rather than by
42
+ * number: it fires only while a retired key still holds a mode, it fires at
43
+ * most once per reader because its own write is what makes the second load find
44
+ * nothing, and it is retired for good by deleting the last entry of
45
+ * `LEGACY_MODE_STORAGE_KEYS`. There is no date and no version constant in it,
46
+ * and the gate reports how many keys are still live so the number is visible
47
+ * rather than remembered.
10
48
  */
11
- import { DEFAULT_STORAGE_KEY, MODES, PACKS } from '../theming';
49
+ export const THEME_MIGRATION_RETIRE = 'if(G==="legacy"){try{localStorage.setItem(K,JSON.stringify({pack:pack,mode:m}));localStorage.removeItem(LK);}catch(e){}}';
12
50
  export function PrismThemeScript({ storageKey = DEFAULT_STORAGE_KEY, defaultPack = 'default', defaultMode = 'light', }) {
13
51
  const script = '(function(){try{' +
14
- 'var root=document.documentElement;' +
15
- `var packs=${JSON.stringify(PACKS)};` +
16
- `var modes=${JSON.stringify(MODES)};` +
17
- 'var stored=null;' +
18
- `try{stored=JSON.parse(localStorage.getItem(${JSON.stringify(storageKey)})||"null");}catch(ignored){}` +
19
- 'var pack=stored&&packs.indexOf(stored.pack)>-1?stored.pack:(root.getAttribute("data-pack")||' +
20
- `${JSON.stringify(defaultPack)});` +
21
- 'var mode=stored&&modes.indexOf(stored.mode)>-1?stored.mode:(root.classList.contains("dark")?"dark":' +
22
- `${JSON.stringify(defaultMode)});` +
23
- `if(packs.indexOf(pack)<0){pack=${JSON.stringify(defaultPack)};}` +
24
- 'if(pack&&pack!=="default"){root.setAttribute("data-pack",pack);}else{root.removeAttribute("data-pack");}' +
25
- 'root.classList.toggle("dark",mode==="dark");' +
26
- '}catch(ignored){}})();';
52
+ 'var r=document.documentElement,' +
53
+ `P=${JSON.stringify(PACKS)},` +
54
+ `M=${JSON.stringify(MODES)},` +
55
+ `K=${JSON.stringify(storageKey)},` +
56
+ `A=${JSON.stringify(PACK_ATTRIBUTE)},` +
57
+ `O=${JSON.stringify(THEME_ORIGIN_ATTRIBUTE)},` +
58
+ `DP=${JSON.stringify(defaultPack)},` +
59
+ `DM=${JSON.stringify(defaultMode)},` +
60
+ 'L=null,LK=null,S=null,T=null;' +
61
+ 'try{var t=localStorage.getItem(K);if(t){T=t;S=JSON.parse(t);}}catch(e){}' +
62
+ THEME_MIGRATION_READ +
63
+ 'var a=r.getAttribute(A),' +
64
+ 'DPK=P.indexOf(a)>-1?a:null,' +
65
+ 'DK=r.classList.contains("dark")?"dark":null,' +
66
+ 'U=S&&typeof S=="object"&&P.indexOf(S.pack)>-1&&M.indexOf(S.mode)>-1,' +
67
+ 'pack,m,G;' +
68
+ 'if(U){pack=S.pack;m=S.mode;G="stored";}' +
69
+ 'else if(T){pack=DPK||DP;m=DK||DM;G="unparsed";}' +
70
+ 'else if(L){pack=DP;m=L;G="legacy";}' +
71
+ 'else if(DPK||DK){pack=DPK||DP;m=DK||DM;G="document";}' +
72
+ 'else{pack=DP;m=DM;G="default";}' +
73
+ THEME_MIGRATION_RETIRE +
74
+ 'if(pack!=="default"){r.setAttribute(A,pack);}else{r.removeAttribute(A);}' +
75
+ 'r.classList.toggle("dark",m==="dark");' +
76
+ 'r.setAttribute(O,G);' +
77
+ '}catch(e){}})();';
27
78
  return _jsx("script", { dangerouslySetInnerHTML: { __html: script } });
28
79
  }