@aster-ui/prefixed 0.12.86 → 0.12.88
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/components/CodeEditor.js +75 -75
- package/dist/components/CodeEditor.js.map +1 -1
- package/dist/components/ContextMenu.d.ts +5 -4
- package/dist/components/ContextMenu.js +112 -119
- package/dist/components/ContextMenu.js.map +1 -1
- package/dist/components/Terminal.js +47 -44
- package/dist/components/Terminal.js.map +1 -1
- package/dist/hooks/useTheme.d.ts +6 -2
- package/dist/hooks/useTheme.js +55 -42
- package/dist/hooks/useTheme.js.map +1 -1
- package/dist/providers/ThemeProvider.d.ts +2 -2
- package/dist/providers/ThemeProvider.js +47 -89
- package/dist/providers/ThemeProvider.js.map +1 -1
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Terminal.js","sources":["../../src/components/Terminal.tsx"],"sourcesContent":["import React, { useEffect, useRef, useImperativeHandle, forwardRef } from 'react'\nimport * as XTermPkg from '@xterm/xterm'\nimport * as FitAddonPkg from '@xterm/addon-fit'\nimport type { Terminal as XTermType, ITerminalOptions, ITerminalInitOnlyOptions } from '@xterm/xterm'\nimport type { FitAddon as FitAddonType } from '@xterm/addon-fit'\nimport { useTheme } from '../hooks/useTheme'\n\n// Handle both ESM and CJS module formats\nconst XTerm = (XTermPkg as { Terminal?: typeof XTermType }).Terminal\n ?? (XTermPkg as { default?: { Terminal: typeof XTermType } }).default?.Terminal\n ?? (XTermPkg as unknown as typeof XTermType)\nconst FitAddon = (FitAddonPkg as { FitAddon?: typeof FitAddonType }).FitAddon\n ?? (FitAddonPkg as { default?: { FitAddon: typeof FitAddonType } }).default?.FitAddon\n ?? (FitAddonPkg as unknown as typeof FitAddonType)\n\n// Inject xterm.css once (inlined for reliability across bundlers)\nlet cssInjected = false\nfunction injectXtermCSS() {\n if (cssInjected || typeof document === 'undefined') return\n cssInjected = true\n\n const style = document.createElement('style')\n style.setAttribute('data-xterm', '')\n style.textContent = `.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{user-select:text;white-space:pre}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:.5}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}`\n document.head.appendChild(style)\n}\n\nexport interface TerminalRef {\n /** The underlying xterm.js Terminal instance */\n terminal: XTermType | null\n /** Write data to the terminal */\n write: (data: string) => void\n /** Write a line to the terminal (with newline) */\n writeln: (data: string) => void\n /** Clear the terminal */\n clear: () => void\n /** Focus the terminal */\n focus: () => void\n /** Fit the terminal to its container */\n fit: () => void\n /** Command history (only available with readline mode) */\n history: string[]\n /** Clear command history (only available with readline mode) */\n clearHistory: () => void\n}\n\nexport interface TerminalProps {\n /** Callback when user types in the terminal (raw input) */\n onData?: (data: string) => void\n /** Callback when terminal is ready */\n onReady?: (terminal: XTermType) => void\n /** Enable readline mode with line editing and history */\n readline?: boolean\n /** Prompt string for readline mode (supports ANSI colors) */\n prompt?: string\n /** Callback when user submits a line in readline mode. Return a Promise to defer the next prompt. */\n onLine?: (line: string) => void | Promise<void>\n /** Convert LF to CRLF for proper newline handling (default: true) */\n convertEol?: boolean\n /** xterm.js options (theme is auto-applied unless you override it) */\n options?: ITerminalOptions & ITerminalInitOnlyOptions\n /** Additional CSS classes for the container */\n className?: string\n /** Container style */\n style?: React.CSSProperties\n 'data-testid'?: string\n}\n\nexport const Terminal = forwardRef<TerminalRef, TerminalProps>(({\n onData,\n onReady,\n readline = false,\n prompt = '$ ',\n onLine,\n convertEol = true,\n options = {},\n className = '',\n style,\n 'data-testid': testId,\n}, ref) => {\n const containerRef = useRef<HTMLDivElement>(null)\n const terminalRef = useRef<XTermType | null>(null)\n const fitAddonRef = useRef<FitAddonType | null>(null)\n const { isDark, colors } = useTheme()\n\n // Readline state\n const readlineState = useRef({\n buffer: '',\n cursor: 0,\n history: [] as string[],\n historyIndex: -1,\n savedBuffer: '',\n })\n\n // Build theme from DaisyUI colors\n const getTheme = () => ({\n background: colors.background,\n foreground: colors.foreground,\n cursor: colors.primary,\n cursorAccent: colors.background,\n selectionBackground: colors.primary + '40',\n selectionForeground: colors.foreground,\n black: isDark ? '#000000' : '#2e3436',\n red: colors.error,\n green: colors.success,\n yellow: colors.warning,\n blue: colors.info,\n magenta: colors.secondary,\n cyan: colors.accent,\n white: isDark ? '#d3d7cf' : '#eeeeec',\n brightBlack: '#555753',\n brightRed: colors.error,\n brightGreen: colors.success,\n brightYellow: colors.warning,\n brightBlue: colors.info,\n brightMagenta: colors.secondary,\n brightCyan: colors.accent,\n brightWhite: isDark ? '#eeeeec' : '#ffffff',\n })\n\n useImperativeHandle(ref, () => ({\n terminal: terminalRef.current,\n write: (data: string) => terminalRef.current?.write(data),\n writeln: (data: string) => terminalRef.current?.writeln(data),\n clear: () => terminalRef.current?.clear(),\n focus: () => terminalRef.current?.focus(),\n fit: () => fitAddonRef.current?.fit(),\n history: readlineState.current.history,\n clearHistory: () => { readlineState.current.history = [] },\n }), [])\n\n // Redraw the current line in readline mode\n const redrawLine = () => {\n const term = terminalRef.current\n if (!term) return\n const s = readlineState.current\n // Move to start of line, clear line, write prompt + buffer, position cursor\n term.write('\\r\\x1b[K' + prompt + s.buffer)\n // Move cursor to correct position\n const moveBack = s.buffer.length - s.cursor\n if (moveBack > 0) {\n term.write(`\\x1b[${moveBack}D`)\n }\n }\n\n // Handle readline input\n const handleReadlineData = (data: string) => {\n const term = terminalRef.current\n if (!term) return\n const s = readlineState.current\n\n // Handle escape sequences (arrow keys)\n if (data === '\\x1b[A') {\n // Up arrow - history previous\n if (s.history.length > 0 && s.historyIndex < s.history.length - 1) {\n if (s.historyIndex === -1) s.savedBuffer = s.buffer\n s.historyIndex++\n s.buffer = s.history[s.history.length - 1 - s.historyIndex]\n s.cursor = s.buffer.length\n redrawLine()\n }\n return\n }\n if (data === '\\x1b[B') {\n // Down arrow - history next\n if (s.historyIndex > -1) {\n s.historyIndex--\n s.buffer = s.historyIndex === -1 ? s.savedBuffer : s.history[s.history.length - 1 - s.historyIndex]\n s.cursor = s.buffer.length\n redrawLine()\n }\n return\n }\n if (data === '\\x1b[C') {\n // Right arrow\n if (s.cursor < s.buffer.length) {\n s.cursor++\n term.write('\\x1b[C')\n }\n return\n }\n if (data === '\\x1b[D') {\n // Left arrow\n if (s.cursor > 0) {\n s.cursor--\n term.write('\\x1b[D')\n }\n return\n }\n if (data === '\\x1b[H' || data === '\\x1bOH' || data === '\\x1b[1~') {\n // Home - move to start of line\n if (s.cursor > 0) {\n term.write(`\\x1b[${s.cursor}D`)\n s.cursor = 0\n }\n return\n }\n if (data === '\\x1b[F' || data === '\\x1bOF' || data === '\\x1b[4~') {\n // End - move to end of line\n if (s.cursor < s.buffer.length) {\n term.write(`\\x1b[${s.buffer.length - s.cursor}C`)\n s.cursor = s.buffer.length\n }\n return\n }\n if (data === '\\x1b[3~') {\n // Delete key - delete character at cursor\n if (s.cursor < s.buffer.length) {\n s.buffer = s.buffer.slice(0, s.cursor) + s.buffer.slice(s.cursor + 1)\n redrawLine()\n }\n return\n }\n\n // Ignore other escape sequences\n if (data.startsWith('\\x1b')) return\n\n if (data === '\\r') {\n // Enter\n term.writeln('')\n const line = s.buffer\n if (line.trim()) {\n s.history.push(line)\n }\n s.buffer = ''\n s.cursor = 0\n s.historyIndex = -1\n s.savedBuffer = ''\n const result = onLine?.(line)\n if (result && typeof (result as any).then === 'function') {\n (result as any).then(() => term.write(prompt))\n } else {\n term.write(prompt)\n }\n } else if (data === '\\x7f' || data === '\\b') {\n // Backspace\n if (s.cursor > 0) {\n s.buffer = s.buffer.slice(0, s.cursor - 1) + s.buffer.slice(s.cursor)\n s.cursor--\n redrawLine()\n }\n } else if (data === '\\x03') {\n // Ctrl+C\n term.writeln('^C')\n s.buffer = ''\n s.cursor = 0\n s.historyIndex = -1\n term.write(prompt)\n } else if (data >= ' ' || data === '\\t') {\n // Printable characters\n s.buffer = s.buffer.slice(0, s.cursor) + data + s.buffer.slice(s.cursor)\n s.cursor += data.length\n redrawLine()\n }\n }\n\n // Initialize terminal\n useEffect(() => {\n if (!containerRef.current) return\n\n injectXtermCSS()\n\n const container = containerRef.current\n let terminal: XTermType | null = null\n let fitAddon: FitAddonType | null = null\n let resizeObserver: ResizeObserver | null = null\n let initialized = false\n let disposed = false\n\n const initTerminal = () => {\n if (initialized || disposed || !container) return\n\n // Check container has dimensions before opening\n const rect = container.getBoundingClientRect()\n if (rect.width === 0 || rect.height === 0) return\n\n initialized = true\n\n terminal = new XTerm({\n theme: getTheme(),\n cursorBlink: true,\n convertEol,\n fontFamily: 'ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace',\n fontSize: 14,\n ...options,\n })\n fitAddon = new FitAddon()\n\n terminal.loadAddon(fitAddon)\n terminal.open(container)\n fitAddon.fit()\n\n terminalRef.current = terminal\n fitAddonRef.current = fitAddon\n\n if (readline) {\n terminal.onData(handleReadlineData)\n } else if (onData) {\n terminal.onData(onData)\n }\n\n onReady?.(terminal)\n\n // Write initial prompt after onReady so welcome messages appear first\n if (readline) {\n terminal.write(prompt)\n }\n }\n\n // Use ResizeObserver to wait for container to have dimensions\n resizeObserver = new ResizeObserver((entries) => {\n const entry = entries[0]\n if (!entry || disposed) return\n\n if (!initialized) {\n initTerminal()\n } else if (fitAddon) {\n fitAddon.fit()\n }\n })\n resizeObserver.observe(container)\n\n // Also try immediately in case container already has dimensions\n requestAnimationFrame(initTerminal)\n\n return () => {\n disposed = true\n resizeObserver?.disconnect()\n terminal?.dispose()\n terminalRef.current = null\n fitAddonRef.current = null\n }\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n // Update theme when colors change\n useEffect(() => {\n if (!terminalRef.current) return\n terminalRef.current.options.theme = getTheme()\n }, [isDark, colors]) // eslint-disable-line react-hooks/exhaustive-deps\n\n return (\n <div\n ref={containerRef}\n className={className}\n style={{ width: '100%', height: '100%', ...style }}\n data-testid={testId}\n />\n )\n})\n\nTerminal.displayName = 'Terminal'\n"],"names":["XTerm","XTermPkg","FitAddon","FitAddonPkg","cssInjected","injectXtermCSS","style","Terminal","forwardRef","onData","onReady","readline","prompt","onLine","convertEol","options","className","testId","ref","containerRef","useRef","terminalRef","fitAddonRef","isDark","colors","useTheme","readlineState","getTheme","useImperativeHandle","data","redrawLine","term","s","moveBack","handleReadlineData","line","result","useEffect","container","terminal","fitAddon","resizeObserver","initialized","disposed","initTerminal","rect","entries","jsx"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Terminal.js","sources":["../../src/components/Terminal.tsx"],"sourcesContent":["import React, { useEffect, useRef, useImperativeHandle, forwardRef } from 'react'\nimport * as XTermPkg from '@xterm/xterm'\nimport * as FitAddonPkg from '@xterm/addon-fit'\nimport type { Terminal as XTermType, ITerminalOptions, ITerminalInitOnlyOptions } from '@xterm/xterm'\nimport type { FitAddon as FitAddonType } from '@xterm/addon-fit'\nimport { useTheme } from '../hooks/useTheme'\n\n// Handle both ESM and CJS module formats\nconst XTerm = (XTermPkg as { Terminal?: typeof XTermType }).Terminal\n ?? (XTermPkg as { default?: { Terminal: typeof XTermType } }).default?.Terminal\n ?? (XTermPkg as unknown as typeof XTermType)\nconst FitAddon = (FitAddonPkg as { FitAddon?: typeof FitAddonType }).FitAddon\n ?? (FitAddonPkg as { default?: { FitAddon: typeof FitAddonType } }).default?.FitAddon\n ?? (FitAddonPkg as unknown as typeof FitAddonType)\n\n// Inject xterm.css once (inlined for reliability across bundlers)\nlet cssInjected = false\nfunction injectXtermCSS() {\n if (cssInjected || typeof document === 'undefined') return\n cssInjected = true\n\n const style = document.createElement('style')\n style.setAttribute('data-xterm', '')\n style.textContent = `.xterm{cursor:text;position:relative;user-select:none;-ms-user-select:none;-webkit-user-select:none}.xterm.focus,.xterm:focus{outline:none}.xterm .xterm-helpers{position:absolute;top:0;z-index:5}.xterm .xterm-helper-textarea{padding:0;border:0;margin:0;position:absolute;opacity:0;left:-9999em;top:0;width:0;height:0;z-index:-5;white-space:nowrap;overflow:hidden;resize:none}.xterm .composition-view{background:#000;color:#fff;display:none;position:absolute;white-space:nowrap;z-index:1}.xterm .composition-view.active{display:block}.xterm .xterm-viewport{background-color:#000;overflow-y:scroll;cursor:default;position:absolute;right:0;left:0;top:0;bottom:0}.xterm .xterm-screen{position:relative}.xterm .xterm-screen canvas{position:absolute;left:0;top:0}.xterm .xterm-scroll-area{visibility:hidden}.xterm-char-measure-element{display:inline-block;visibility:hidden;position:absolute;top:0;left:-9999em;line-height:normal}.xterm.enable-mouse-events{cursor:default}.xterm.xterm-cursor-pointer,.xterm .xterm-cursor-pointer{cursor:pointer}.xterm.column-select.focus{cursor:crosshair}.xterm .xterm-accessibility:not(.debug),.xterm .xterm-message{position:absolute;left:0;top:0;bottom:0;right:0;z-index:10;color:transparent;pointer-events:none}.xterm .xterm-accessibility-tree:not(.debug) *::selection{color:transparent}.xterm .xterm-accessibility-tree{user-select:text;white-space:pre}.xterm .live-region{position:absolute;left:-9999px;width:1px;height:1px;overflow:hidden}.xterm-dim{opacity:.5}.xterm-underline-1{text-decoration:underline}.xterm-underline-2{text-decoration:double underline}.xterm-underline-3{text-decoration:wavy underline}.xterm-underline-4{text-decoration:dotted underline}.xterm-underline-5{text-decoration:dashed underline}.xterm-overline{text-decoration:overline}.xterm-overline.xterm-underline-1{text-decoration:overline underline}.xterm-overline.xterm-underline-2{text-decoration:overline double underline}.xterm-overline.xterm-underline-3{text-decoration:overline wavy underline}.xterm-overline.xterm-underline-4{text-decoration:overline dotted underline}.xterm-overline.xterm-underline-5{text-decoration:overline dashed underline}.xterm-strikethrough{text-decoration:line-through}.xterm-screen .xterm-decoration-container .xterm-decoration{z-index:6;position:absolute}.xterm-screen .xterm-decoration-container .xterm-decoration.xterm-decoration-top-layer{z-index:7}.xterm-decoration-overview-ruler{z-index:8;position:absolute;top:0;right:0;pointer-events:none}.xterm-decoration-top{z-index:2;position:relative}`\n document.head.appendChild(style)\n}\n\nexport interface TerminalRef {\n /** The underlying xterm.js Terminal instance */\n terminal: XTermType | null\n /** Write data to the terminal */\n write: (data: string) => void\n /** Write a line to the terminal (with newline) */\n writeln: (data: string) => void\n /** Clear the terminal */\n clear: () => void\n /** Focus the terminal */\n focus: () => void\n /** Fit the terminal to its container */\n fit: () => void\n /** Command history (only available with readline mode) */\n history: string[]\n /** Clear command history (only available with readline mode) */\n clearHistory: () => void\n}\n\nexport interface TerminalProps {\n /** Callback when user types in the terminal (raw input) */\n onData?: (data: string) => void\n /** Callback when terminal is ready */\n onReady?: (terminal: XTermType) => void\n /** Enable readline mode with line editing and history */\n readline?: boolean\n /** Prompt string for readline mode (supports ANSI colors) */\n prompt?: string\n /** Callback when user submits a line in readline mode. Return a Promise to defer the next prompt. */\n onLine?: (line: string) => void | Promise<void>\n /** Convert LF to CRLF for proper newline handling (default: true) */\n convertEol?: boolean\n /** xterm.js options (theme is auto-applied unless you override it) */\n options?: ITerminalOptions & ITerminalInitOnlyOptions\n /** Additional CSS classes for the container */\n className?: string\n /** Container style */\n style?: React.CSSProperties\n 'data-testid'?: string\n}\n\nexport const Terminal = forwardRef<TerminalRef, TerminalProps>(({\n onData,\n onReady,\n readline = false,\n prompt = '$ ',\n onLine,\n convertEol = true,\n options = {},\n className = '',\n style,\n 'data-testid': testId,\n}, ref) => {\n const containerRef = useRef<HTMLDivElement>(null)\n const terminalRef = useRef<XTermType | null>(null)\n const fitAddonRef = useRef<FitAddonType | null>(null)\n const { isDark, colors } = useTheme()\n\n // Readline state\n const readlineState = useRef({\n buffer: '',\n cursor: 0,\n history: [] as string[],\n historyIndex: -1,\n savedBuffer: '',\n })\n\n // Build theme from DaisyUI colors\n const getTheme = () => ({\n background: colors.background,\n foreground: colors.foreground,\n cursor: colors.foreground,\n cursorAccent: colors.background,\n selectionBackground: colors.primary + '40',\n selectionForeground: colors.foreground,\n black: isDark ? '#000000' : '#2e3436',\n red: colors.error,\n green: colors.success,\n yellow: colors.warning,\n blue: colors.info,\n magenta: colors.secondary,\n cyan: colors.accent,\n white: isDark ? '#d3d7cf' : '#eeeeec',\n brightBlack: '#555753',\n brightRed: colors.error,\n brightGreen: colors.success,\n brightYellow: colors.warning,\n brightBlue: colors.info,\n brightMagenta: colors.secondary,\n brightCyan: colors.accent,\n brightWhite: isDark ? '#eeeeec' : '#ffffff',\n })\n\n useImperativeHandle(ref, () => ({\n terminal: terminalRef.current,\n write: (data: string) => terminalRef.current?.write(data),\n writeln: (data: string) => terminalRef.current?.writeln(data),\n clear: () => terminalRef.current?.clear(),\n focus: () => terminalRef.current?.focus(),\n fit: () => fitAddonRef.current?.fit(),\n history: readlineState.current.history,\n clearHistory: () => { readlineState.current.history = [] },\n }), [])\n\n // Redraw the current line in readline mode\n const redrawLine = () => {\n const term = terminalRef.current\n if (!term) return\n const s = readlineState.current\n // Move to start of line, clear line, write prompt + buffer, position cursor\n term.write('\\r\\x1b[K' + prompt + s.buffer)\n // Move cursor to correct position\n const moveBack = s.buffer.length - s.cursor\n if (moveBack > 0) {\n term.write(`\\x1b[${moveBack}D`)\n }\n }\n\n // Handle readline input\n const handleReadlineData = (data: string) => {\n const term = terminalRef.current\n if (!term) return\n const s = readlineState.current\n\n // Handle escape sequences (arrow keys)\n if (data === '\\x1b[A') {\n // Up arrow - history previous\n if (s.history.length > 0 && s.historyIndex < s.history.length - 1) {\n if (s.historyIndex === -1) s.savedBuffer = s.buffer\n s.historyIndex++\n s.buffer = s.history[s.history.length - 1 - s.historyIndex]\n s.cursor = s.buffer.length\n redrawLine()\n }\n return\n }\n if (data === '\\x1b[B') {\n // Down arrow - history next\n if (s.historyIndex > -1) {\n s.historyIndex--\n s.buffer = s.historyIndex === -1 ? s.savedBuffer : s.history[s.history.length - 1 - s.historyIndex]\n s.cursor = s.buffer.length\n redrawLine()\n }\n return\n }\n if (data === '\\x1b[C') {\n // Right arrow\n if (s.cursor < s.buffer.length) {\n s.cursor++\n term.write('\\x1b[C')\n }\n return\n }\n if (data === '\\x1b[D') {\n // Left arrow\n if (s.cursor > 0) {\n s.cursor--\n term.write('\\x1b[D')\n }\n return\n }\n if (data === '\\x1b[H' || data === '\\x1bOH' || data === '\\x1b[1~') {\n // Home - move to start of line\n if (s.cursor > 0) {\n term.write(`\\x1b[${s.cursor}D`)\n s.cursor = 0\n }\n return\n }\n if (data === '\\x1b[F' || data === '\\x1bOF' || data === '\\x1b[4~') {\n // End - move to end of line\n if (s.cursor < s.buffer.length) {\n term.write(`\\x1b[${s.buffer.length - s.cursor}C`)\n s.cursor = s.buffer.length\n }\n return\n }\n if (data === '\\x1b[3~') {\n // Delete key - delete character at cursor\n if (s.cursor < s.buffer.length) {\n s.buffer = s.buffer.slice(0, s.cursor) + s.buffer.slice(s.cursor + 1)\n redrawLine()\n }\n return\n }\n\n // Ignore other escape sequences\n if (data.startsWith('\\x1b')) return\n\n if (data === '\\r') {\n // Enter\n term.writeln('')\n const line = s.buffer\n if (line.trim()) {\n s.history.push(line)\n }\n s.buffer = ''\n s.cursor = 0\n s.historyIndex = -1\n s.savedBuffer = ''\n const result = onLine?.(line)\n if (result && typeof (result as any).then === 'function') {\n (result as any).then(() => term.write(prompt))\n } else {\n term.write(prompt)\n }\n } else if (data === '\\x7f' || data === '\\b') {\n // Backspace\n if (s.cursor > 0) {\n s.buffer = s.buffer.slice(0, s.cursor - 1) + s.buffer.slice(s.cursor)\n s.cursor--\n redrawLine()\n }\n } else if (data === '\\x03') {\n // Ctrl+C\n term.writeln('^C')\n s.buffer = ''\n s.cursor = 0\n s.historyIndex = -1\n term.write(prompt)\n } else if (data >= ' ' || data === '\\t') {\n // Printable characters\n s.buffer = s.buffer.slice(0, s.cursor) + data + s.buffer.slice(s.cursor)\n s.cursor += data.length\n redrawLine()\n }\n }\n\n // Initialize terminal\n useEffect(() => {\n if (!containerRef.current) return\n\n injectXtermCSS()\n\n const container = containerRef.current\n let terminal: XTermType | null = null\n let fitAddon: FitAddonType | null = null\n let resizeObserver: ResizeObserver | null = null\n let initialized = false\n let disposed = false\n\n const initTerminal = () => {\n if (initialized || disposed || !container) return\n\n // Check container has dimensions before opening\n const rect = container.getBoundingClientRect()\n if (rect.width === 0 || rect.height === 0) return\n\n initialized = true\n\n const { theme: userTheme, ...restOptions } = options\n terminal = new XTerm({\n theme: { ...getTheme(), ...userTheme },\n cursorBlink: true,\n convertEol,\n fontFamily: 'ui-monospace, SFMono-Regular, \"SF Mono\", Menlo, Consolas, \"Liberation Mono\", monospace',\n fontSize: 14,\n ...restOptions,\n })\n fitAddon = new FitAddon()\n\n terminal.loadAddon(fitAddon)\n terminal.open(container)\n fitAddon.fit()\n\n terminalRef.current = terminal\n fitAddonRef.current = fitAddon\n\n if (readline) {\n terminal.onData(handleReadlineData)\n } else if (onData) {\n terminal.onData(onData)\n }\n\n onReady?.(terminal)\n\n // Write initial prompt after onReady so welcome messages appear first\n if (readline) {\n terminal.write(prompt)\n }\n }\n\n // Use ResizeObserver to wait for container to have dimensions\n resizeObserver = new ResizeObserver((entries) => {\n const entry = entries[0]\n if (!entry || disposed) return\n\n if (!initialized) {\n initTerminal()\n } else if (fitAddon) {\n fitAddon.fit()\n }\n })\n resizeObserver.observe(container)\n\n // Also try immediately in case container already has dimensions\n requestAnimationFrame(initTerminal)\n\n return () => {\n disposed = true\n resizeObserver?.disconnect()\n terminal?.dispose()\n terminalRef.current = null\n fitAddonRef.current = null\n }\n }, []) // eslint-disable-line react-hooks/exhaustive-deps\n\n // Update theme when colors change\n useEffect(() => {\n if (!terminalRef.current) return\n terminalRef.current.options.theme = { ...getTheme(), ...options.theme }\n }, [isDark, colors]) // eslint-disable-line react-hooks/exhaustive-deps\n\n return (\n <div\n ref={containerRef}\n className={className}\n style={{ width: '100%', height: '100%', ...style }}\n data-testid={testId}\n />\n )\n})\n\nTerminal.displayName = 'Terminal'\n"],"names":["XTerm","XTermPkg","FitAddon","FitAddonPkg","cssInjected","injectXtermCSS","style","Terminal","forwardRef","onData","onReady","readline","prompt","onLine","convertEol","options","className","testId","ref","containerRef","useRef","terminalRef","fitAddonRef","isDark","colors","useTheme","readlineState","getTheme","useImperativeHandle","data","redrawLine","term","s","moveBack","handleReadlineData","line","result","useEffect","container","terminal","fitAddon","resizeObserver","initialized","disposed","initTerminal","rect","userTheme","restOptions","entries","jsx"],"mappings":"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"}
|
package/dist/hooks/useTheme.d.ts
CHANGED
|
@@ -9,7 +9,9 @@ export interface UseThemeReturn {
|
|
|
9
9
|
isDark: boolean;
|
|
10
10
|
/** Set the theme. Only available with ThemeProvider. */
|
|
11
11
|
setTheme: ((theme: string) => void) | undefined;
|
|
12
|
-
/**
|
|
12
|
+
/** Toggle between light and dark. Only available with ThemeProvider. */
|
|
13
|
+
toggleTheme: (() => void) | undefined;
|
|
14
|
+
/** Computed theme colors as hex values. Lazy — only computed when accessed. */
|
|
13
15
|
colors: ThemeColors;
|
|
14
16
|
/** The system preference. Only available with ThemeProvider. */
|
|
15
17
|
systemTheme: 'light' | 'dark' | undefined;
|
|
@@ -24,11 +26,13 @@ export interface UseThemeReturn {
|
|
|
24
26
|
* to isDark and colors based on the current data-theme attribute and
|
|
25
27
|
* system preference.
|
|
26
28
|
*
|
|
29
|
+
* Colors are lazy — only computed (via canvas) when you access them.
|
|
30
|
+
* Components that only need isDark/setTheme pay no cost for color computation.
|
|
31
|
+
*
|
|
27
32
|
* @example
|
|
28
33
|
* // With ThemeProvider (full control)
|
|
29
34
|
* const { theme, setTheme, resolvedTheme, isDark, colors } = useTheme()
|
|
30
35
|
* setTheme('dark')
|
|
31
|
-
* setTheme('system')
|
|
32
36
|
*
|
|
33
37
|
* @example
|
|
34
38
|
* // Without ThemeProvider (read-only)
|
package/dist/hooks/useTheme.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { useHasThemeProvider as
|
|
3
|
-
const
|
|
1
|
+
import { useMemo as i, useState as d, useEffect as l } from "react";
|
|
2
|
+
import { useHasThemeProvider as h, useThemeContext as y } from "../providers/ThemeProvider.js";
|
|
3
|
+
const g = /* @__PURE__ */ new Set([
|
|
4
4
|
"dark",
|
|
5
5
|
"synthwave",
|
|
6
6
|
"halloween",
|
|
@@ -14,15 +14,15 @@ const h = /* @__PURE__ */ new Set([
|
|
|
14
14
|
"dim",
|
|
15
15
|
"sunset"
|
|
16
16
|
]);
|
|
17
|
-
function
|
|
17
|
+
function b(o) {
|
|
18
18
|
if (typeof document > "u") return "#000000";
|
|
19
19
|
const e = document.createElement("canvas");
|
|
20
20
|
e.width = e.height = 1;
|
|
21
21
|
const t = e.getContext("2d");
|
|
22
22
|
if (!t) return "#000000";
|
|
23
|
-
t.fillStyle =
|
|
24
|
-
const [
|
|
25
|
-
return `#${((1 << 24) + (
|
|
23
|
+
t.fillStyle = o, t.fillRect(0, 0, 1, 1);
|
|
24
|
+
const [r, n, s] = t.getImageData(0, 0, 1, 1).data;
|
|
25
|
+
return `#${((1 << 24) + (r << 16) + (n << 8) + s).toString(16).slice(1)}`;
|
|
26
26
|
}
|
|
27
27
|
function a() {
|
|
28
28
|
if (typeof document > "u")
|
|
@@ -38,9 +38,9 @@ function a() {
|
|
|
38
38
|
warning: "#fbbd23",
|
|
39
39
|
error: "#f87272"
|
|
40
40
|
};
|
|
41
|
-
const
|
|
42
|
-
const
|
|
43
|
-
return
|
|
41
|
+
const o = getComputedStyle(document.documentElement), e = (t, r) => {
|
|
42
|
+
const n = o.getPropertyValue(t).trim();
|
|
43
|
+
return n ? b(n) : r;
|
|
44
44
|
};
|
|
45
45
|
return {
|
|
46
46
|
background: e("--color-base-100", "#ffffff"),
|
|
@@ -55,62 +55,75 @@ function a() {
|
|
|
55
55
|
error: e("--color-error", "#f87272")
|
|
56
56
|
};
|
|
57
57
|
}
|
|
58
|
-
function
|
|
58
|
+
function T() {
|
|
59
59
|
return typeof window > "u" ? "light" : window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
60
60
|
}
|
|
61
|
-
function
|
|
61
|
+
function f() {
|
|
62
62
|
return typeof document > "u" ? null : document.documentElement.getAttribute("data-theme");
|
|
63
63
|
}
|
|
64
|
+
function u(o) {
|
|
65
|
+
let e = null;
|
|
66
|
+
return new Proxy({}, {
|
|
67
|
+
get(t, r) {
|
|
68
|
+
return e || (e = a()), e[r];
|
|
69
|
+
},
|
|
70
|
+
ownKeys() {
|
|
71
|
+
return e || (e = a()), Object.keys(e);
|
|
72
|
+
},
|
|
73
|
+
getOwnPropertyDescriptor(t, r) {
|
|
74
|
+
if (e || (e = a()), r in e)
|
|
75
|
+
return { configurable: !0, enumerable: !0, value: e[r] };
|
|
76
|
+
}
|
|
77
|
+
});
|
|
78
|
+
}
|
|
64
79
|
function k() {
|
|
65
|
-
if (
|
|
66
|
-
const e =
|
|
80
|
+
if (h()) {
|
|
81
|
+
const e = y(), t = i(() => u(e.resolvedTheme), [e.resolvedTheme]);
|
|
67
82
|
return {
|
|
68
83
|
theme: e.theme,
|
|
69
84
|
resolvedTheme: e.resolvedTheme,
|
|
70
85
|
isDark: e.isDark,
|
|
71
86
|
setTheme: e.setTheme,
|
|
72
|
-
|
|
87
|
+
toggleTheme: e.toggleTheme,
|
|
88
|
+
colors: t,
|
|
73
89
|
systemTheme: e.systemTheme
|
|
74
90
|
};
|
|
75
91
|
}
|
|
76
|
-
return
|
|
92
|
+
return v();
|
|
77
93
|
}
|
|
78
|
-
function
|
|
79
|
-
const [
|
|
94
|
+
function v() {
|
|
95
|
+
const [o, e] = d(() => ({
|
|
80
96
|
isDark: !1,
|
|
81
|
-
|
|
97
|
+
themeKey: f()
|
|
82
98
|
}));
|
|
83
|
-
return
|
|
99
|
+
return l(() => {
|
|
84
100
|
const t = () => {
|
|
85
|
-
const s =
|
|
101
|
+
const s = f(), m = T();
|
|
86
102
|
let c = !1;
|
|
87
|
-
s ? c =
|
|
88
|
-
requestAnimationFrame(() => {
|
|
89
|
-
e({
|
|
90
|
-
isDark: c,
|
|
91
|
-
colors: a()
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
});
|
|
103
|
+
s ? c = g.has(s) : c = m === "dark", e({ isDark: c, themeKey: s });
|
|
95
104
|
};
|
|
96
105
|
t();
|
|
97
|
-
const
|
|
98
|
-
|
|
106
|
+
const r = new MutationObserver(t);
|
|
107
|
+
r.observe(document.documentElement, {
|
|
99
108
|
attributes: !0,
|
|
100
109
|
attributeFilter: ["data-theme", "class"]
|
|
101
110
|
});
|
|
102
|
-
const
|
|
103
|
-
return
|
|
104
|
-
|
|
111
|
+
const n = window.matchMedia("(prefers-color-scheme: dark)");
|
|
112
|
+
return n.addEventListener("change", t), () => {
|
|
113
|
+
r.disconnect(), n.removeEventListener("change", t);
|
|
114
|
+
};
|
|
115
|
+
}, []), i(() => {
|
|
116
|
+
const t = u(o.themeKey);
|
|
117
|
+
return {
|
|
118
|
+
theme: void 0,
|
|
119
|
+
resolvedTheme: void 0,
|
|
120
|
+
isDark: o.isDark,
|
|
121
|
+
setTheme: void 0,
|
|
122
|
+
toggleTheme: void 0,
|
|
123
|
+
colors: t,
|
|
124
|
+
systemTheme: void 0
|
|
105
125
|
};
|
|
106
|
-
}, [
|
|
107
|
-
theme: void 0,
|
|
108
|
-
resolvedTheme: void 0,
|
|
109
|
-
isDark: r.isDark,
|
|
110
|
-
setTheme: void 0,
|
|
111
|
-
colors: r.colors,
|
|
112
|
-
systemTheme: void 0
|
|
113
|
-
}), [r.isDark, r.colors]);
|
|
126
|
+
}, [o.isDark, o.themeKey]);
|
|
114
127
|
}
|
|
115
128
|
export {
|
|
116
129
|
k as default,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useTheme.js","sources":["../../src/hooks/useTheme.ts"],"sourcesContent":["import { useEffect, useState, useMemo } from 'react'\nimport { useHasThemeProvider, useThemeContext, type ThemeColors } from '../providers/ThemeProvider'\n\nexport type { ThemeColors }\n\n// Common dark themes in DaisyUI\nconst DARK_THEMES = new Set([\n 'dark', 'synthwave', 'halloween', 'forest', 'black', 'luxury', 'dracula',\n 'business', 'night', 'coffee', 'dim', 'sunset'\n])\n\nexport interface UseThemeReturn {\n /** The theme setting (what user selected). Only available with ThemeProvider. */\n theme: string | undefined\n /** The actual applied theme. Only available with ThemeProvider. */\n resolvedTheme: string | undefined\n /** Whether dark mode is active */\n isDark: boolean\n /** Set the theme. Only available with ThemeProvider. */\n setTheme: ((theme: string) => void) | undefined\n /** Computed theme colors as hex values */\n colors: ThemeColors\n /** The system preference. Only available with ThemeProvider. */\n systemTheme: 'light' | 'dark' | undefined\n}\n\n// Convert any CSS color to hex\nfunction colorToHex(color: string): string {\n if (typeof document === 'undefined') return '#000000'\n const canvas = document.createElement('canvas')\n canvas.width = canvas.height = 1\n const ctx = canvas.getContext('2d')\n if (!ctx) return '#000000'\n ctx.fillStyle = color\n ctx.fillRect(0, 0, 1, 1)\n const [r, g, b] = ctx.getImageData(0, 0, 1, 1).data\n return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`\n}\n\nfunction getThemeColors(): ThemeColors {\n if (typeof document === 'undefined') {\n return {\n background: '#ffffff',\n foreground: '#000000',\n primary: '#6366f1',\n primaryContent: '#ffffff',\n secondary: '#f000b8',\n accent: '#37cdbe',\n info: '#3abff8',\n success: '#36d399',\n warning: '#fbbd23',\n error: '#f87272',\n }\n }\n\n const style = getComputedStyle(document.documentElement)\n const getColor = (varName: string, fallback: string): string => {\n const value = style.getPropertyValue(varName).trim()\n return value ? colorToHex(value) : fallback\n }\n\n return {\n background: getColor('--color-base-100', '#ffffff'),\n foreground: getColor('--color-base-content', '#000000'),\n primary: getColor('--color-primary', '#6366f1'),\n primaryContent: getColor('--color-primary-content', '#ffffff'),\n secondary: getColor('--color-secondary', '#f000b8'),\n accent: getColor('--color-accent', '#37cdbe'),\n info: getColor('--color-info', '#3abff8'),\n success: getColor('--color-success', '#36d399'),\n warning: getColor('--color-warning', '#fbbd23'),\n error: getColor('--color-error', '#f87272'),\n }\n}\n\nfunction getSystemTheme(): 'light' | 'dark' {\n if (typeof window === 'undefined') return 'light'\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'\n}\n\nfunction getCurrentTheme(): string | null {\n if (typeof document === 'undefined') return null\n return document.documentElement.getAttribute('data-theme')\n}\n\n/**\n * Hook to detect current theme and get computed colors.\n *\n * When used within a ThemeProvider, returns full theme control including\n * setTheme, theme selection, and resolved theme.\n *\n * When used standalone (without ThemeProvider), provides read-only access\n * to isDark and colors based on the current data-theme attribute and\n * system preference.\n *\n * @example\n * // With ThemeProvider (full control)\n * const { theme, setTheme, resolvedTheme, isDark, colors } = useTheme()\n * setTheme('dark')\n * setTheme('system')\n *\n * @example\n * // Without ThemeProvider (read-only)\n * const { isDark, colors } = useTheme()\n * // colors.primary, colors.foreground, etc.\n */\nexport function useTheme(): UseThemeReturn {\n const hasProvider = useHasThemeProvider()\n\n // If we have a provider, use its context\n if (hasProvider) {\n // This is safe because hasProvider is stable after initial render\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const context = useThemeContext()\n return {\n theme: context.theme,\n resolvedTheme: context.resolvedTheme,\n isDark: context.isDark,\n setTheme: context.setTheme,\n colors: context.colors,\n systemTheme: context.systemTheme,\n }\n }\n\n // Standalone mode - no provider\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return useThemeStandalone()\n}\n\n/**\n * Standalone theme detection (no ThemeProvider)\n */\nfunction useThemeStandalone(): UseThemeReturn {\n const [state, setState] = useState<{ isDark: boolean; colors: ThemeColors }>(() => ({\n isDark: false,\n colors: getThemeColors(),\n }))\n\n useEffect(() => {\n const updateTheme = () => {\n const currentTheme = getCurrentTheme()\n const systemTheme = getSystemTheme()\n\n // Determine if dark based on data-theme or system preference\n let isDark = false\n if (currentTheme) {\n isDark = DARK_THEMES.has(currentTheme)\n } else {\n isDark = systemTheme === 'dark'\n }\n\n // Double RAF ensures CSS has fully recalculated\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n setState({\n isDark,\n colors: getThemeColors(),\n })\n })\n })\n }\n\n updateTheme()\n\n // Watch for theme changes via attribute mutation\n const observer = new MutationObserver(updateTheme)\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['data-theme', 'class']\n })\n\n // Watch for system preference changes\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')\n mediaQuery.addEventListener('change', updateTheme)\n\n return () => {\n observer.disconnect()\n mediaQuery.removeEventListener('change', updateTheme)\n }\n }, [])\n\n return useMemo(() => ({\n theme: undefined,\n resolvedTheme: undefined,\n isDark: state.isDark,\n setTheme: undefined,\n colors: state.colors,\n systemTheme: undefined,\n }), [state.isDark, state.colors])\n}\n\nexport default useTheme\n"],"names":["DARK_THEMES","colorToHex","color","canvas","ctx","r","g","b","getThemeColors","style","getColor","varName","fallback","value","getSystemTheme","getCurrentTheme","useTheme","useHasThemeProvider","context","useThemeContext","useThemeStandalone","state","setState","useState","useEffect","updateTheme","currentTheme","systemTheme","isDark","observer","mediaQuery","useMemo"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useTheme.js","sources":["../../src/hooks/useTheme.ts"],"sourcesContent":["import { useEffect, useState, useMemo } from 'react'\nimport { useHasThemeProvider, useThemeContext, type ThemeColors } from '../providers/ThemeProvider'\n\nexport type { ThemeColors }\n\n// Common dark themes in DaisyUI\nconst DARK_THEMES = new Set([\n 'dark', 'synthwave', 'halloween', 'forest', 'black', 'luxury', 'dracula',\n 'business', 'night', 'coffee', 'dim', 'sunset'\n])\n\nexport interface UseThemeReturn {\n /** The theme setting (what user selected). Only available with ThemeProvider. */\n theme: string | undefined\n /** The actual applied theme. Only available with ThemeProvider. */\n resolvedTheme: string | undefined\n /** Whether dark mode is active */\n isDark: boolean\n /** Set the theme. Only available with ThemeProvider. */\n setTheme: ((theme: string) => void) | undefined\n /** Toggle between light and dark. Only available with ThemeProvider. */\n toggleTheme: (() => void) | undefined\n /** Computed theme colors as hex values. Lazy — only computed when accessed. */\n colors: ThemeColors\n /** The system preference. Only available with ThemeProvider. */\n systemTheme: 'light' | 'dark' | undefined\n}\n\n// Convert any CSS color to hex via canvas\nfunction colorToHex(color: string): string {\n if (typeof document === 'undefined') return '#000000'\n const canvas = document.createElement('canvas')\n canvas.width = canvas.height = 1\n const ctx = canvas.getContext('2d')\n if (!ctx) return '#000000'\n ctx.fillStyle = color\n ctx.fillRect(0, 0, 1, 1)\n const [r, g, b] = ctx.getImageData(0, 0, 1, 1).data\n return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`\n}\n\nfunction getThemeColors(): ThemeColors {\n if (typeof document === 'undefined') {\n return {\n background: '#ffffff',\n foreground: '#000000',\n primary: '#6366f1',\n primaryContent: '#ffffff',\n secondary: '#f000b8',\n accent: '#37cdbe',\n info: '#3abff8',\n success: '#36d399',\n warning: '#fbbd23',\n error: '#f87272',\n }\n }\n\n const style = getComputedStyle(document.documentElement)\n const getColor = (varName: string, fallback: string): string => {\n const value = style.getPropertyValue(varName).trim()\n return value ? colorToHex(value) : fallback\n }\n\n return {\n background: getColor('--color-base-100', '#ffffff'),\n foreground: getColor('--color-base-content', '#000000'),\n primary: getColor('--color-primary', '#6366f1'),\n primaryContent: getColor('--color-primary-content', '#ffffff'),\n secondary: getColor('--color-secondary', '#f000b8'),\n accent: getColor('--color-accent', '#37cdbe'),\n info: getColor('--color-info', '#3abff8'),\n success: getColor('--color-success', '#36d399'),\n warning: getColor('--color-warning', '#fbbd23'),\n error: getColor('--color-error', '#f87272'),\n }\n}\n\nfunction getSystemTheme(): 'light' | 'dark' {\n if (typeof window === 'undefined') return 'light'\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'\n}\n\nfunction getCurrentTheme(): string | null {\n if (typeof document === 'undefined') return null\n return document.documentElement.getAttribute('data-theme')\n}\n\n/**\n * Create a lazy colors object that only computes hex values when accessed.\n * Returns a new object each time so referential identity tracks the dep key.\n */\nfunction createLazyColors(depKey: unknown): ThemeColors {\n let cached: ThemeColors | null = null\n // depKey is used only to invalidate the closure identity\n void depKey\n return new Proxy({} as ThemeColors, {\n get(_target, prop: string) {\n if (!cached) cached = getThemeColors()\n return cached[prop as keyof ThemeColors]\n },\n ownKeys() {\n if (!cached) cached = getThemeColors()\n return Object.keys(cached)\n },\n getOwnPropertyDescriptor(_target, prop) {\n if (!cached) cached = getThemeColors()\n if (prop in cached) {\n return { configurable: true, enumerable: true, value: cached[prop as keyof ThemeColors] }\n }\n },\n })\n}\n\n/**\n * Hook to detect current theme and get computed colors.\n *\n * When used within a ThemeProvider, returns full theme control including\n * setTheme, theme selection, and resolved theme.\n *\n * When used standalone (without ThemeProvider), provides read-only access\n * to isDark and colors based on the current data-theme attribute and\n * system preference.\n *\n * Colors are lazy — only computed (via canvas) when you access them.\n * Components that only need isDark/setTheme pay no cost for color computation.\n *\n * @example\n * // With ThemeProvider (full control)\n * const { theme, setTheme, resolvedTheme, isDark, colors } = useTheme()\n * setTheme('dark')\n *\n * @example\n * // Without ThemeProvider (read-only)\n * const { isDark, colors } = useTheme()\n * // colors.primary, colors.foreground, etc.\n */\nexport function useTheme(): UseThemeReturn {\n const hasProvider = useHasThemeProvider()\n\n if (hasProvider) {\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const context = useThemeContext()\n // eslint-disable-next-line react-hooks/rules-of-hooks\n const lazyColors = useMemo(() => createLazyColors(context.resolvedTheme), [context.resolvedTheme])\n return {\n theme: context.theme,\n resolvedTheme: context.resolvedTheme,\n isDark: context.isDark,\n setTheme: context.setTheme,\n toggleTheme: context.toggleTheme,\n colors: lazyColors,\n systemTheme: context.systemTheme,\n }\n }\n\n // eslint-disable-next-line react-hooks/rules-of-hooks\n return useThemeStandalone()\n}\n\n/**\n * Standalone theme detection (no ThemeProvider)\n */\nfunction useThemeStandalone(): UseThemeReturn {\n const [state, setState] = useState<{ isDark: boolean; themeKey: string | null }>(() => ({\n isDark: false,\n themeKey: getCurrentTheme(),\n }))\n\n useEffect(() => {\n const updateTheme = () => {\n const currentTheme = getCurrentTheme()\n const systemTheme = getSystemTheme()\n\n let isDark = false\n if (currentTheme) {\n isDark = DARK_THEMES.has(currentTheme)\n } else {\n isDark = systemTheme === 'dark'\n }\n\n setState({ isDark, themeKey: currentTheme })\n }\n\n updateTheme()\n\n const observer = new MutationObserver(updateTheme)\n observer.observe(document.documentElement, {\n attributes: true,\n attributeFilter: ['data-theme', 'class']\n })\n\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')\n mediaQuery.addEventListener('change', updateTheme)\n\n return () => {\n observer.disconnect()\n mediaQuery.removeEventListener('change', updateTheme)\n }\n }, [])\n\n return useMemo(() => {\n const lazyColors = createLazyColors(state.themeKey)\n return {\n theme: undefined,\n resolvedTheme: undefined,\n isDark: state.isDark,\n setTheme: undefined,\n toggleTheme: undefined,\n colors: lazyColors,\n systemTheme: undefined,\n }\n }, [state.isDark, state.themeKey])\n}\n\nexport default useTheme\n"],"names":["DARK_THEMES","colorToHex","color","canvas","ctx","g","b","getThemeColors","style","getColor","varName","fallback","value","getSystemTheme","getCurrentTheme","createLazyColors","depKey","cached","_target","prop","useTheme","useHasThemeProvider","context","useThemeContext","lazyColors","useMemo","useThemeStandalone","state","setState","useState","useEffect","updateTheme","currentTheme","systemTheme","isDark","observer","mediaQuery"],"mappings":";;AAMA,MAAMA,wBAAkB,IAAI;AAAA,EAC1B;AAAA,EAAQ;AAAA,EAAa;AAAA,EAAa;AAAA,EAAU;AAAA,EAAS;AAAA,EAAU;AAAA,EAC/D;AAAA,EAAY;AAAA,EAAS;AAAA,EAAU;AAAA,EAAO;AACxC,CAAC;AAoBD,SAASC,EAAWC,GAAuB;AACzC,MAAI,OAAO,WAAa,IAAa,QAAO;AAC5C,QAAMC,IAAS,SAAS,cAAc,QAAQ;AAC9C,EAAAA,EAAO,QAAQA,EAAO,SAAS;AAC/B,QAAMC,IAAMD,EAAO,WAAW,IAAI;AAClC,MAAI,CAACC,EAAK,QAAO;AACjB,EAAAA,EAAI,YAAYF,GAChBE,EAAI,SAAS,GAAG,GAAG,GAAG,CAAC;AACvB,QAAM,CAAC,GAAGC,GAAGC,CAAC,IAAIF,EAAI,aAAa,GAAG,GAAG,GAAG,CAAC,EAAE;AAC/C,SAAO,MAAM,KAAK,OAAO,KAAK,OAAOC,KAAK,KAAKC,GAAG,SAAS,EAAE,EAAE,MAAM,CAAC,CAAC;AACzE;AAEA,SAASC,IAA8B;AACrC,MAAI,OAAO,WAAa;AACtB,WAAO;AAAA,MACL,YAAY;AAAA,MACZ,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,gBAAgB;AAAA,MAChB,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,SAAS;AAAA,MACT,SAAS;AAAA,MACT,OAAO;AAAA,IAAA;AAIX,QAAMC,IAAQ,iBAAiB,SAAS,eAAe,GACjDC,IAAW,CAACC,GAAiBC,MAA6B;AAC9D,UAAMC,IAAQJ,EAAM,iBAAiBE,CAAO,EAAE,KAAA;AAC9C,WAAOE,IAAQX,EAAWW,CAAK,IAAID;AAAA,EACrC;AAEA,SAAO;AAAA,IACL,YAAYF,EAAS,oBAAoB,SAAS;AAAA,IAClD,YAAYA,EAAS,wBAAwB,SAAS;AAAA,IACtD,SAASA,EAAS,mBAAmB,SAAS;AAAA,IAC9C,gBAAgBA,EAAS,2BAA2B,SAAS;AAAA,IAC7D,WAAWA,EAAS,qBAAqB,SAAS;AAAA,IAClD,QAAQA,EAAS,kBAAkB,SAAS;AAAA,IAC5C,MAAMA,EAAS,gBAAgB,SAAS;AAAA,IACxC,SAASA,EAAS,mBAAmB,SAAS;AAAA,IAC9C,SAASA,EAAS,mBAAmB,SAAS;AAAA,IAC9C,OAAOA,EAAS,iBAAiB,SAAS;AAAA,EAAA;AAE9C;AAEA,SAASI,IAAmC;AAC1C,SAAI,OAAO,SAAW,MAAoB,UACnC,OAAO,WAAW,8BAA8B,EAAE,UAAU,SAAS;AAC9E;AAEA,SAASC,IAAiC;AACxC,SAAI,OAAO,WAAa,MAAoB,OACrC,SAAS,gBAAgB,aAAa,YAAY;AAC3D;AAMA,SAASC,EAAiBC,GAA8B;AACtD,MAAIC,IAA6B;AAGjC,SAAO,IAAI,MAAM,IAAmB;AAAA,IAClC,IAAIC,GAASC,GAAc;AACzB,aAAKF,MAAQA,IAASV,EAAA,IACfU,EAAOE,CAAyB;AAAA,IACzC;AAAA,IACA,UAAU;AACR,aAAKF,MAAQA,IAASV,EAAA,IACf,OAAO,KAAKU,CAAM;AAAA,IAC3B;AAAA,IACA,yBAAyBC,GAASC,GAAM;AAEtC,UADKF,MAAQA,IAASV,EAAA,IAClBY,KAAQF;AACV,eAAO,EAAE,cAAc,IAAM,YAAY,IAAM,OAAOA,EAAOE,CAAyB,EAAA;AAAA,IAE1F;AAAA,EAAA,CACD;AACH;AAyBO,SAASC,IAA2B;AAGzC,MAFoBC,EAAA,GAEH;AAEf,UAAMC,IAAUC,EAAA,GAEVC,IAAaC,EAAQ,MAAMV,EAAiBO,EAAQ,aAAa,GAAG,CAACA,EAAQ,aAAa,CAAC;AACjG,WAAO;AAAA,MACL,OAAOA,EAAQ;AAAA,MACf,eAAeA,EAAQ;AAAA,MACvB,QAAQA,EAAQ;AAAA,MAChB,UAAUA,EAAQ;AAAA,MAClB,aAAaA,EAAQ;AAAA,MACrB,QAAQE;AAAA,MACR,aAAaF,EAAQ;AAAA,IAAA;AAAA,EAEzB;AAGA,SAAOI,EAAA;AACT;AAKA,SAASA,IAAqC;AAC5C,QAAM,CAACC,GAAOC,CAAQ,IAAIC,EAAuD,OAAO;AAAA,IACtF,QAAQ;AAAA,IACR,UAAUf,EAAA;AAAA,EAAgB,EAC1B;AAEF,SAAAgB,EAAU,MAAM;AACd,UAAMC,IAAc,MAAM;AACxB,YAAMC,IAAelB,EAAA,GACfmB,IAAcpB,EAAA;AAEpB,UAAIqB,IAAS;AACb,MAAIF,IACFE,IAASlC,EAAY,IAAIgC,CAAY,IAErCE,IAASD,MAAgB,QAG3BL,EAAS,EAAE,QAAAM,GAAQ,UAAUF,EAAA,CAAc;AAAA,IAC7C;AAEA,IAAAD,EAAA;AAEA,UAAMI,IAAW,IAAI,iBAAiBJ,CAAW;AACjD,IAAAI,EAAS,QAAQ,SAAS,iBAAiB;AAAA,MACzC,YAAY;AAAA,MACZ,iBAAiB,CAAC,cAAc,OAAO;AAAA,IAAA,CACxC;AAED,UAAMC,IAAa,OAAO,WAAW,8BAA8B;AACnE,WAAAA,EAAW,iBAAiB,UAAUL,CAAW,GAE1C,MAAM;AACX,MAAAI,EAAS,WAAA,GACTC,EAAW,oBAAoB,UAAUL,CAAW;AAAA,IACtD;AAAA,EACF,GAAG,CAAA,CAAE,GAEEN,EAAQ,MAAM;AACnB,UAAMD,IAAaT,EAAiBY,EAAM,QAAQ;AAClD,WAAO;AAAA,MACL,OAAO;AAAA,MACP,eAAe;AAAA,MACf,QAAQA,EAAM;AAAA,MACd,UAAU;AAAA,MACV,aAAa;AAAA,MACb,QAAQH;AAAA,MACR,aAAa;AAAA,IAAA;AAAA,EAEjB,GAAG,CAACG,EAAM,QAAQA,EAAM,QAAQ,CAAC;AACnC;"}
|
|
@@ -33,8 +33,8 @@ export interface ThemeContextValue {
|
|
|
33
33
|
isDark: boolean;
|
|
34
34
|
/** Set the theme */
|
|
35
35
|
setTheme: (theme: string) => void;
|
|
36
|
-
/**
|
|
37
|
-
|
|
36
|
+
/** Toggle between light and dark */
|
|
37
|
+
toggleTheme: () => void;
|
|
38
38
|
/** The system preference ("light" or "dark") */
|
|
39
39
|
systemTheme: 'light' | 'dark';
|
|
40
40
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { jsx as
|
|
2
|
-
import { useState as
|
|
3
|
-
const
|
|
1
|
+
import { jsx as M } from "react/jsx-runtime";
|
|
2
|
+
import { useState as S, useMemo as m, useEffect as l, useCallback as y, createContext as P, useContext as g } from "react";
|
|
3
|
+
const C = /* @__PURE__ */ new Set([
|
|
4
4
|
"dark",
|
|
5
5
|
"synthwave",
|
|
6
6
|
"halloween",
|
|
@@ -13,52 +13,11 @@ const A = /* @__PURE__ */ new Set([
|
|
|
13
13
|
"coffee",
|
|
14
14
|
"dim",
|
|
15
15
|
"sunset"
|
|
16
|
-
]),
|
|
17
|
-
function
|
|
18
|
-
if (typeof document > "u") return "#000000";
|
|
19
|
-
const e = document.createElement("canvas");
|
|
20
|
-
e.width = e.height = 1;
|
|
21
|
-
const n = e.getContext("2d");
|
|
22
|
-
if (!n) return "#000000";
|
|
23
|
-
n.fillStyle = t, n.fillRect(0, 0, 1, 1);
|
|
24
|
-
const [f, o, i] = n.getImageData(0, 0, 1, 1).data;
|
|
25
|
-
return `#${((1 << 24) + (f << 16) + (o << 8) + i).toString(16).slice(1)}`;
|
|
26
|
-
}
|
|
27
|
-
function b() {
|
|
28
|
-
if (typeof document > "u")
|
|
29
|
-
return {
|
|
30
|
-
background: "#ffffff",
|
|
31
|
-
foreground: "#000000",
|
|
32
|
-
primary: "#6366f1",
|
|
33
|
-
primaryContent: "#ffffff",
|
|
34
|
-
secondary: "#f000b8",
|
|
35
|
-
accent: "#37cdbe",
|
|
36
|
-
info: "#3abff8",
|
|
37
|
-
success: "#36d399",
|
|
38
|
-
warning: "#fbbd23",
|
|
39
|
-
error: "#f87272"
|
|
40
|
-
};
|
|
41
|
-
const t = getComputedStyle(document.documentElement), e = (n, f) => {
|
|
42
|
-
const o = t.getPropertyValue(n).trim();
|
|
43
|
-
return o ? L(o) : f;
|
|
44
|
-
};
|
|
45
|
-
return {
|
|
46
|
-
background: e("--color-base-100", "#ffffff"),
|
|
47
|
-
foreground: e("--color-base-content", "#000000"),
|
|
48
|
-
primary: e("--color-primary", "#6366f1"),
|
|
49
|
-
primaryContent: e("--color-primary-content", "#ffffff"),
|
|
50
|
-
secondary: e("--color-secondary", "#f000b8"),
|
|
51
|
-
accent: e("--color-accent", "#37cdbe"),
|
|
52
|
-
info: e("--color-info", "#3abff8"),
|
|
53
|
-
success: e("--color-success", "#36d399"),
|
|
54
|
-
warning: e("--color-warning", "#fbbd23"),
|
|
55
|
-
error: e("--color-error", "#f87272")
|
|
56
|
-
};
|
|
57
|
-
}
|
|
58
|
-
function M() {
|
|
16
|
+
]), w = P(void 0);
|
|
17
|
+
function k() {
|
|
59
18
|
return typeof window > "u" ? "light" : window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light";
|
|
60
19
|
}
|
|
61
|
-
function
|
|
20
|
+
function T(t) {
|
|
62
21
|
if (!t || typeof window > "u") return null;
|
|
63
22
|
try {
|
|
64
23
|
return localStorage.getItem(t);
|
|
@@ -66,65 +25,64 @@ function H(t) {
|
|
|
66
25
|
return null;
|
|
67
26
|
}
|
|
68
27
|
}
|
|
69
|
-
function
|
|
28
|
+
function b(t, f) {
|
|
70
29
|
if (!(!t || typeof window > "u"))
|
|
71
30
|
try {
|
|
72
|
-
localStorage.setItem(t,
|
|
31
|
+
localStorage.setItem(t, f);
|
|
73
32
|
} catch {
|
|
74
33
|
}
|
|
75
34
|
}
|
|
76
|
-
function
|
|
35
|
+
function V({
|
|
77
36
|
children: t,
|
|
78
|
-
defaultTheme:
|
|
79
|
-
storageKey:
|
|
80
|
-
lightTheme:
|
|
81
|
-
darkTheme:
|
|
82
|
-
isDarkTheme:
|
|
37
|
+
defaultTheme: f = "system",
|
|
38
|
+
storageKey: r = "asterui-theme",
|
|
39
|
+
lightTheme: c = "light",
|
|
40
|
+
darkTheme: d = "dark",
|
|
41
|
+
isDarkTheme: s
|
|
83
42
|
}) {
|
|
84
|
-
const [u,
|
|
85
|
-
w(r), I(n, r);
|
|
86
|
-
}, [n]);
|
|
43
|
+
const [u, I] = S(k), [i, a] = S(() => T(r) || f), o = m(() => i === "system" ? u === "dark" ? d : c : i, [i, u, c, d]), h = m(() => s ? s(o) : C.has(o), [o, s]);
|
|
87
44
|
l(() => {
|
|
88
|
-
typeof document > "u" ||
|
|
89
|
-
|
|
90
|
-
S(b());
|
|
91
|
-
});
|
|
92
|
-
}));
|
|
93
|
-
}, [c]), l(() => {
|
|
45
|
+
typeof document > "u" || document.documentElement.setAttribute("data-theme", o);
|
|
46
|
+
}, [o]), l(() => {
|
|
94
47
|
if (typeof window > "u") return;
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
};
|
|
98
|
-
return r.addEventListener("change", s), () => r.removeEventListener("change", s);
|
|
48
|
+
const e = window.matchMedia("(prefers-color-scheme: dark)"), n = (x) => I(x.matches ? "dark" : "light");
|
|
49
|
+
return e.addEventListener("change", n), () => e.removeEventListener("change", n);
|
|
99
50
|
}, []), l(() => {
|
|
100
|
-
if (!
|
|
101
|
-
const
|
|
102
|
-
|
|
51
|
+
if (!r || typeof window > "u") return;
|
|
52
|
+
const e = (n) => {
|
|
53
|
+
n.key === r && n.newValue && a(n.newValue);
|
|
103
54
|
};
|
|
104
|
-
return window.addEventListener("storage",
|
|
105
|
-
}, [
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
}
|
|
114
|
-
|
|
55
|
+
return window.addEventListener("storage", e), () => window.removeEventListener("storage", e);
|
|
56
|
+
}, [r]);
|
|
57
|
+
const v = y((e) => {
|
|
58
|
+
a(e), b(r, e);
|
|
59
|
+
}, [r]), p = y(() => {
|
|
60
|
+
a((e) => {
|
|
61
|
+
const n = e === "system" ? u === "dark" ? d : c : e, E = (s ? s(n) : C.has(n)) ? c : d;
|
|
62
|
+
return b(r, E), E;
|
|
63
|
+
});
|
|
64
|
+
}, [u, c, d, s, r]), L = m(() => ({
|
|
65
|
+
theme: i,
|
|
66
|
+
resolvedTheme: o,
|
|
67
|
+
isDark: h,
|
|
68
|
+
setTheme: v,
|
|
69
|
+
toggleTheme: p,
|
|
70
|
+
systemTheme: u
|
|
71
|
+
}), [i, o, h, v, p, u]);
|
|
72
|
+
return /* @__PURE__ */ M(w.Provider, { value: L, children: t });
|
|
115
73
|
}
|
|
116
|
-
function
|
|
117
|
-
const t =
|
|
74
|
+
function j() {
|
|
75
|
+
const t = g(w);
|
|
118
76
|
if (!t)
|
|
119
77
|
throw new Error("useThemeContext must be used within a ThemeProvider");
|
|
120
78
|
return t;
|
|
121
79
|
}
|
|
122
|
-
function
|
|
123
|
-
return
|
|
80
|
+
function q() {
|
|
81
|
+
return g(w) !== void 0;
|
|
124
82
|
}
|
|
125
83
|
export {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
84
|
+
V as ThemeProvider,
|
|
85
|
+
q as useHasThemeProvider,
|
|
86
|
+
j as useThemeContext
|
|
129
87
|
};
|
|
130
88
|
//# sourceMappingURL=ThemeProvider.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ThemeProvider.js","sources":["../../src/providers/ThemeProvider.tsx"],"sourcesContent":["import React, { createContext, useContext, useEffect, useState, useCallback, useMemo } from 'react'\n\n// Common dark themes in DaisyUI\nconst DARK_THEMES = new Set([\n 'dark', 'synthwave', 'halloween', 'forest', 'black', 'luxury', 'dracula',\n 'business', 'night', 'coffee', 'dim', 'sunset'\n])\n\nexport interface ThemeProviderProps {\n children: React.ReactNode\n /** Default theme. Use \"system\" to follow browser preference. */\n defaultTheme?: string\n /** localStorage key for persisting theme. Set to false to disable persistence. */\n storageKey?: string | false\n /** Light theme to use when system preference is light */\n lightTheme?: string\n /** Dark theme to use when system preference is dark */\n darkTheme?: string\n /** Custom function to determine if a theme is dark */\n isDarkTheme?: (theme: string) => boolean\n}\n\nexport interface ThemeColors {\n background: string\n foreground: string\n primary: string\n primaryContent: string\n secondary: string\n accent: string\n info: string\n success: string\n warning: string\n error: string\n}\n\nexport interface ThemeContextValue {\n /** The theme setting (what user selected: \"system\", \"light\", \"dark\", etc.) */\n theme: string\n /** The actual applied theme after resolving \"system\" */\n resolvedTheme: string\n /** Whether the resolved theme is dark */\n isDark: boolean\n /** Set the theme */\n setTheme: (theme: string) => void\n /** Computed theme colors as hex values (for canvas/non-CSS contexts) */\n colors: ThemeColors\n /** The system preference (\"light\" or \"dark\") */\n systemTheme: 'light' | 'dark'\n}\n\nconst ThemeContext = createContext<ThemeContextValue | undefined>(undefined)\n\n// Convert any CSS color to hex\nfunction colorToHex(color: string): string {\n if (typeof document === 'undefined') return '#000000'\n const canvas = document.createElement('canvas')\n canvas.width = canvas.height = 1\n const ctx = canvas.getContext('2d')\n if (!ctx) return '#000000'\n ctx.fillStyle = color\n ctx.fillRect(0, 0, 1, 1)\n const [r, g, b] = ctx.getImageData(0, 0, 1, 1).data\n return `#${((1 << 24) + (r << 16) + (g << 8) + b).toString(16).slice(1)}`\n}\n\nfunction getThemeColors(): ThemeColors {\n if (typeof document === 'undefined') {\n return {\n background: '#ffffff',\n foreground: '#000000',\n primary: '#6366f1',\n primaryContent: '#ffffff',\n secondary: '#f000b8',\n accent: '#37cdbe',\n info: '#3abff8',\n success: '#36d399',\n warning: '#fbbd23',\n error: '#f87272',\n }\n }\n\n const style = getComputedStyle(document.documentElement)\n const getColor = (varName: string, fallback: string): string => {\n const value = style.getPropertyValue(varName).trim()\n return value ? colorToHex(value) : fallback\n }\n\n return {\n background: getColor('--color-base-100', '#ffffff'),\n foreground: getColor('--color-base-content', '#000000'),\n primary: getColor('--color-primary', '#6366f1'),\n primaryContent: getColor('--color-primary-content', '#ffffff'),\n secondary: getColor('--color-secondary', '#f000b8'),\n accent: getColor('--color-accent', '#37cdbe'),\n info: getColor('--color-info', '#3abff8'),\n success: getColor('--color-success', '#36d399'),\n warning: getColor('--color-warning', '#fbbd23'),\n error: getColor('--color-error', '#f87272'),\n }\n}\n\nfunction getSystemTheme(): 'light' | 'dark' {\n if (typeof window === 'undefined') return 'light'\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'\n}\n\nfunction getStoredTheme(key: string | false): string | null {\n if (!key || typeof window === 'undefined') return null\n try {\n return localStorage.getItem(key)\n } catch {\n return null\n }\n}\n\nfunction storeTheme(key: string | false, theme: string): void {\n if (!key || typeof window === 'undefined') return\n try {\n localStorage.setItem(key, theme)\n } catch {\n // Ignore storage errors\n }\n}\n\nexport function ThemeProvider({\n children,\n defaultTheme = 'system',\n storageKey = 'asterui-theme',\n lightTheme = 'light',\n darkTheme = 'dark',\n isDarkTheme,\n}: ThemeProviderProps) {\n // Initialize theme from storage or default\n const [theme, setThemeState] = useState<string>(() => {\n const stored = getStoredTheme(storageKey)\n return stored || defaultTheme\n })\n\n // Track system preference\n const [systemTheme, setSystemTheme] = useState<'light' | 'dark'>(getSystemTheme)\n\n // Resolve the actual theme\n const resolvedTheme = useMemo(() => {\n if (theme === 'system') {\n return systemTheme === 'dark' ? darkTheme : lightTheme\n }\n return theme\n }, [theme, systemTheme, lightTheme, darkTheme])\n\n // Determine if dark\n const isDark = useMemo(() => {\n if (isDarkTheme) return isDarkTheme(resolvedTheme)\n return DARK_THEMES.has(resolvedTheme)\n }, [resolvedTheme, isDarkTheme])\n\n // Track colors (updated after theme applies)\n const [colors, setColors] = useState<ThemeColors>(getThemeColors)\n\n // Set theme function\n const setTheme = useCallback((newTheme: string) => {\n setThemeState(newTheme)\n storeTheme(storageKey, newTheme)\n }, [storageKey])\n\n // Apply theme to document\n useEffect(() => {\n if (typeof document === 'undefined') return\n document.documentElement.setAttribute('data-theme', resolvedTheme)\n\n // Double RAF ensures CSS has fully recalculated after attribute change\n requestAnimationFrame(() => {\n requestAnimationFrame(() => {\n setColors(getThemeColors())\n })\n })\n }, [resolvedTheme])\n\n // Listen for system preference changes\n useEffect(() => {\n if (typeof window === 'undefined') return\n\n const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)')\n const handleChange = (e: MediaQueryListEvent) => {\n setSystemTheme(e.matches ? 'dark' : 'light')\n }\n\n mediaQuery.addEventListener('change', handleChange)\n return () => mediaQuery.removeEventListener('change', handleChange)\n }, [])\n\n // Listen for storage changes (cross-tab sync)\n useEffect(() => {\n if (!storageKey || typeof window === 'undefined') return\n\n const handleStorage = (e: StorageEvent) => {\n if (e.key === storageKey && e.newValue) {\n setThemeState(e.newValue)\n }\n }\n\n window.addEventListener('storage', handleStorage)\n return () => window.removeEventListener('storage', handleStorage)\n }, [storageKey])\n\n const value = useMemo<ThemeContextValue>(() => ({\n theme,\n resolvedTheme,\n isDark,\n setTheme,\n colors,\n systemTheme,\n }), [theme, resolvedTheme, isDark, setTheme, colors, systemTheme])\n\n return (\n <ThemeContext.Provider value={value}>\n {children}\n </ThemeContext.Provider>\n )\n}\n\n/**\n * Hook to access theme context.\n * Must be used within a ThemeProvider.\n */\nexport function useThemeContext(): ThemeContextValue {\n const context = useContext(ThemeContext)\n if (!context) {\n throw new Error('useThemeContext must be used within a ThemeProvider')\n }\n return context\n}\n\n/**\n * Check if ThemeProvider is present in the tree.\n */\nexport function useHasThemeProvider(): boolean {\n return useContext(ThemeContext) !== undefined\n}\n"],"names":["DARK_THEMES","ThemeContext","createContext","colorToHex","color","canvas","ctx","r","g","b","getThemeColors","style","getColor","varName","fallback","value","getSystemTheme","getStoredTheme","key","storeTheme","theme","ThemeProvider","children","defaultTheme","storageKey","lightTheme","darkTheme","isDarkTheme","setThemeState","useState","systemTheme","setSystemTheme","resolvedTheme","useMemo","isDark","colors","setColors","setTheme","useCallback","newTheme","useEffect","mediaQuery","handleChange","e","handleStorage","jsx","useThemeContext","context","useContext","useHasThemeProvider"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"ThemeProvider.js","sources":["../../src/providers/ThemeProvider.tsx"],"sourcesContent":["import React, { createContext, useContext, useEffect, useState, useCallback, useMemo } from 'react'\n\n// Common dark themes in DaisyUI\nconst DARK_THEMES = new Set([\n 'dark', 'synthwave', 'halloween', 'forest', 'black', 'luxury', 'dracula',\n 'business', 'night', 'coffee', 'dim', 'sunset'\n])\n\nexport interface ThemeProviderProps {\n children: React.ReactNode\n /** Default theme. Use \"system\" to follow browser preference. */\n defaultTheme?: string\n /** localStorage key for persisting theme. Set to false to disable persistence. */\n storageKey?: string | false\n /** Light theme to use when system preference is light */\n lightTheme?: string\n /** Dark theme to use when system preference is dark */\n darkTheme?: string\n /** Custom function to determine if a theme is dark */\n isDarkTheme?: (theme: string) => boolean\n}\n\nexport interface ThemeColors {\n background: string\n foreground: string\n primary: string\n primaryContent: string\n secondary: string\n accent: string\n info: string\n success: string\n warning: string\n error: string\n}\n\nexport interface ThemeContextValue {\n /** The theme setting (what user selected: \"system\", \"light\", \"dark\", etc.) */\n theme: string\n /** The actual applied theme after resolving \"system\" */\n resolvedTheme: string\n /** Whether the resolved theme is dark */\n isDark: boolean\n /** Set the theme */\n setTheme: (theme: string) => void\n /** Toggle between light and dark */\n toggleTheme: () => void\n /** The system preference (\"light\" or \"dark\") */\n systemTheme: 'light' | 'dark'\n}\n\nconst ThemeContext = createContext<ThemeContextValue | undefined>(undefined)\n\nfunction getSystemTheme(): 'light' | 'dark' {\n if (typeof window === 'undefined') return 'light'\n return window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light'\n}\n\nfunction getStoredTheme(key: string | false): string | null {\n if (!key || typeof window === 'undefined') return null\n try {\n return localStorage.getItem(key)\n } catch {\n return null\n }\n}\n\nfunction storeTheme(key: string | false, theme: string): void {\n if (!key || typeof window === 'undefined') return\n try {\n localStorage.setItem(key, theme)\n } catch {\n // Ignore storage errors\n }\n}\n\nexport function ThemeProvider({\n children,\n defaultTheme = 'system',\n storageKey = 'asterui-theme',\n lightTheme = 'light',\n darkTheme = 'dark',\n isDarkTheme,\n}: ThemeProviderProps) {\n const [systemTheme, setSystemTheme] = useState<'light' | 'dark'>(getSystemTheme)\n\n const [theme, setThemeState] = useState<string>(() => {\n return getStoredTheme(storageKey) || defaultTheme\n })\n\n const resolvedTheme = useMemo(() => {\n if (theme === 'system') {\n return systemTheme === 'dark' ? darkTheme : lightTheme\n }\n return theme\n }, [theme, systemTheme, lightTheme, darkTheme])\n\n const isDark = useMemo(() => {\n if (isDarkTheme) return isDarkTheme(resolvedTheme)\n return DARK_THEMES.has(resolvedTheme)\n }, [resolvedTheme, isDarkTheme])\n\n // Set data-theme on <html>\n useEffect(() => {\n if (typeof document === 'undefined') return\n document.documentElement.setAttribute('data-theme', resolvedTheme)\n }, [resolvedTheme])\n\n // Listen for system preference changes\n useEffect(() => {\n if (typeof window === 'undefined') return\n const mq = window.matchMedia('(prefers-color-scheme: dark)')\n const handler = (e: MediaQueryListEvent) => setSystemTheme(e.matches ? 'dark' : 'light')\n mq.addEventListener('change', handler)\n return () => mq.removeEventListener('change', handler)\n }, [])\n\n // Listen for storage changes (cross-tab sync)\n useEffect(() => {\n if (!storageKey || typeof window === 'undefined') return\n const handler = (e: StorageEvent) => {\n if (e.key === storageKey && e.newValue) {\n setThemeState(e.newValue)\n }\n }\n window.addEventListener('storage', handler)\n return () => window.removeEventListener('storage', handler)\n }, [storageKey])\n\n const setTheme = useCallback((t: string) => {\n setThemeState(t)\n storeTheme(storageKey, t)\n }, [storageKey])\n\n const toggleTheme = useCallback(() => {\n setThemeState((current) => {\n const resolved = current === 'system'\n ? (systemTheme === 'dark' ? darkTheme : lightTheme)\n : current\n const currentIsDark = isDarkTheme ? isDarkTheme(resolved) : DARK_THEMES.has(resolved)\n const next = currentIsDark ? lightTheme : darkTheme\n storeTheme(storageKey, next)\n return next\n })\n }, [systemTheme, lightTheme, darkTheme, isDarkTheme, storageKey])\n\n const value = useMemo<ThemeContextValue>(() => ({\n theme, resolvedTheme, isDark, setTheme, toggleTheme, systemTheme,\n }), [theme, resolvedTheme, isDark, setTheme, toggleTheme, systemTheme])\n\n return (\n <ThemeContext.Provider value={value}>\n {children}\n </ThemeContext.Provider>\n )\n}\n\n/**\n * Hook to access theme context.\n * Must be used within a ThemeProvider.\n */\nexport function useThemeContext(): ThemeContextValue {\n const context = useContext(ThemeContext)\n if (!context) {\n throw new Error('useThemeContext must be used within a ThemeProvider')\n }\n return context\n}\n\n/**\n * Check if ThemeProvider is present in the tree.\n */\nexport function useHasThemeProvider(): boolean {\n return useContext(ThemeContext) !== undefined\n}\n"],"names":["DARK_THEMES","ThemeContext","createContext","getSystemTheme","getStoredTheme","key","storeTheme","theme","ThemeProvider","children","defaultTheme","storageKey","lightTheme","darkTheme","isDarkTheme","systemTheme","setSystemTheme","useState","setThemeState","resolvedTheme","useMemo","isDark","useEffect","mq","handler","e","setTheme","useCallback","t","toggleTheme","current","resolved","next","value","jsx","useThemeContext","context","useContext","useHasThemeProvider"],"mappings":"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"}
|