preview.tsx 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112
  1. import "@kirincode-ai/ui/styles/tailwind"
  2. import "@kirincode-ai/session-ui/styles"
  3. import "@kirincode-ai/ui/v2/styles/tailwind.css"
  4. import { createEffect, onCleanup, onMount } from "solid-js"
  5. import addonA11y from "@storybook/addon-a11y"
  6. import addonDocs from "@storybook/addon-docs"
  7. import { MetaProvider } from "@solidjs/meta"
  8. import { addons } from "storybook/preview-api"
  9. import { GLOBALS_UPDATED } from "storybook/internal/core-events"
  10. import { createJSXDecorator, definePreview } from "storybook-solidjs-vite"
  11. import { DialogProvider } from "@kirincode-ai/ui/context/dialog"
  12. import { MarkedProvider } from "@kirincode-ai/ui/context/marked"
  13. import { ThemeProvider, useTheme, type ColorScheme } from "@kirincode-ai/ui/theme"
  14. import { Font } from "@kirincode-ai/ui/font"
  15. function resolveScheme(value: unknown): ColorScheme {
  16. if (value === "light" || value === "dark" || value === "system") return value
  17. return "system"
  18. }
  19. const channel = addons.getChannel()
  20. const Scheme = (props: { value?: unknown }) => {
  21. const theme = useTheme()
  22. const apply = (value?: unknown) => {
  23. theme.setColorScheme(resolveScheme(value))
  24. }
  25. createEffect(() => {
  26. apply(props.value)
  27. })
  28. createEffect(() => {
  29. const root = document.documentElement
  30. root.classList.remove("light", "dark")
  31. root.classList.add(theme.mode())
  32. })
  33. onMount(() => {
  34. const handler = (event: { globals?: Record<string, unknown> }) => {
  35. apply(event.globals?.theme)
  36. }
  37. channel.on(GLOBALS_UPDATED, handler)
  38. onCleanup(() => channel.off(GLOBALS_UPDATED, handler))
  39. })
  40. return null
  41. }
  42. const NewLayout = () => {
  43. // Mirror app.tsx BodyDesignClass so stories render with v2 (new-layout) styles
  44. // instead of the legacy `body:not([data-new-layout])` branch.
  45. onMount(() => {
  46. document.body.toggleAttribute("data-new-layout", true)
  47. document.body.classList.add("font-(family-name:--font-family-text)", "text-[13px]", "font-[440]")
  48. document.body.classList.remove("text-12-regular")
  49. })
  50. return null
  51. }
  52. const frame = createJSXDecorator((Story, context) => {
  53. const override = context.parameters?.themes?.themeOverride
  54. const selected = context.globals?.theme
  55. const pick = override === "light" || override === "dark" ? override : selected
  56. const scheme = resolveScheme(pick)
  57. return (
  58. <MetaProvider>
  59. <Font />
  60. <ThemeProvider>
  61. <Scheme value={scheme} />
  62. <NewLayout />
  63. <DialogProvider>
  64. <MarkedProvider>
  65. <div
  66. style={{
  67. "min-height": "100vh",
  68. padding: "24px",
  69. "background-color": "var(--background-base)",
  70. color: "var(--text-base)",
  71. }}
  72. >
  73. <Story />
  74. </div>
  75. </MarkedProvider>
  76. </DialogProvider>
  77. </ThemeProvider>
  78. </MetaProvider>
  79. )
  80. })
  81. export default definePreview({
  82. addons: [addonDocs(), addonA11y()],
  83. decorators: [frame],
  84. globalTypes: {
  85. theme: {
  86. name: "Theme",
  87. description: "Global theme",
  88. defaultValue: "light",
  89. },
  90. },
  91. parameters: {
  92. actions: {
  93. argTypesRegex: "^on.*",
  94. },
  95. controls: {
  96. matchers: {
  97. color: /(background|color)$/i,
  98. date: /Date$/i,
  99. },
  100. },
  101. a11y: {
  102. test: "todo",
  103. },
  104. },
  105. })