@lukoweb/apitogo 0.2.22 → 0.2.24
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/README.md +94 -94
- package/cli.js +13 -13
- package/client.d.ts +8 -8
- package/dist/cli/cli.js +561 -232
- package/dist/declarations/cli/platform/client/platform-api.d.ts +18 -0
- package/dist/declarations/config/validators/InputNavigationSchema.d.ts +7 -7
- package/dist/declarations/config/validators/ZudokuConfig.d.ts +1 -1
- package/docs/cli/commands.md +75 -75
- package/docs/cli/go-live.md +123 -123
- package/docs/cli/overview.md +91 -91
- package/docs/cli/setup.md +141 -141
- package/docs/cli/shape.md +127 -127
- package/docs/components/alert.mdx +130 -130
- package/docs/components/badge.mdx +70 -70
- package/docs/components/button.mdx +132 -132
- package/docs/components/callout.mdx +112 -112
- package/docs/components/card.mdx +104 -104
- package/docs/components/checkbox.mdx +72 -72
- package/docs/components/client-only.mdx +79 -79
- package/docs/components/code-tabs.mdx +179 -179
- package/docs/components/dialog.mdx +167 -167
- package/docs/components/head.mdx +200 -200
- package/docs/components/icons.mdx +27 -27
- package/docs/components/input.mdx +96 -96
- package/docs/components/label.mdx +86 -86
- package/docs/components/link.mdx +242 -242
- package/docs/components/markdown.mdx +151 -151
- package/docs/components/mermaid.mdx +81 -81
- package/docs/components/playground.mdx +85 -85
- package/docs/components/secret.mdx +79 -79
- package/docs/components/select.mdx +176 -176
- package/docs/components/shadcn.mdx +73 -73
- package/docs/components/slider.mdx +108 -108
- package/docs/components/slot.mdx +119 -119
- package/docs/components/stepper.mdx +138 -138
- package/docs/components/switch.mdx +96 -96
- package/docs/components/syntax-highlight.mdx +602 -602
- package/docs/components/textarea.mdx +78 -78
- package/docs/components/tooltip.mdx +195 -195
- package/docs/components/typography.mdx +61 -61
- package/docs/components.mdx +60 -60
- package/docs/concepts/auth-provider-api-identities.md +105 -105
- package/docs/configuration/ai-assistants.md +65 -65
- package/docs/configuration/api-catalog.md +109 -109
- package/docs/configuration/api-reference.md +397 -397
- package/docs/configuration/authentication-auth0.md +181 -181
- package/docs/configuration/authentication-azure-ad.md +245 -245
- package/docs/configuration/authentication-clerk.md +117 -117
- package/docs/configuration/authentication-firebase.md +69 -69
- package/docs/configuration/authentication-pingfederate.md +143 -143
- package/docs/configuration/authentication-supabase.md +233 -233
- package/docs/configuration/authentication.md +159 -159
- package/docs/configuration/build-configuration.mdx +147 -147
- package/docs/configuration/docs.md +282 -282
- package/docs/configuration/footer.mdx +118 -118
- package/docs/configuration/gateway.md +221 -221
- package/docs/configuration/llms.md +90 -90
- package/docs/configuration/make-config.mdx +124 -124
- package/docs/configuration/manifest.md +128 -128
- package/docs/configuration/navigation.mdx +421 -421
- package/docs/configuration/overview.md +166 -166
- package/docs/configuration/plans.md +153 -153
- package/docs/configuration/protected-routes.md +150 -150
- package/docs/configuration/search.md +164 -164
- package/docs/configuration/sentry.mdx +44 -44
- package/docs/configuration/site.md +124 -124
- package/docs/configuration/slots.mdx +125 -125
- package/docs/configuration/vite-config.md +18 -18
- package/docs/custom-plugins.md +285 -285
- package/docs/customization/colors-theme.mdx +278 -278
- package/docs/customization/fonts.md +110 -110
- package/docs/customization/landing-page.md +188 -188
- package/docs/deploy/apache-nginx.md +41 -41
- package/docs/deploy/apitogo.md +17 -17
- package/docs/deploy/cloudflare-pages.md +74 -74
- package/docs/deploy/direct-upload.md +17 -17
- package/docs/deploy/github-pages.md +49 -49
- package/docs/deploy/vercel.md +101 -101
- package/docs/deployment.md +9 -9
- package/docs/extending/events.md +124 -124
- package/docs/guides/custom-pages.md +110 -110
- package/docs/guides/environment-variables.md +94 -94
- package/docs/guides/managing-api-keys-and-identities.md +89 -89
- package/docs/guides/mermaid.mdx +70 -70
- package/docs/guides/navigation-rules.mdx +203 -203
- package/docs/guides/processors.mdx +234 -234
- package/docs/guides/static-files.md +55 -55
- package/docs/guides/transforming-examples.md +156 -156
- package/docs/guides/using-multiple-apis.md +87 -87
- package/docs/markdown/admonitions.md +128 -128
- package/docs/markdown/code-blocks.md +196 -196
- package/docs/markdown/frontmatter.md +173 -173
- package/docs/markdown/mdx.md +68 -68
- package/docs/markdown/overview.md +275 -275
- package/docs/mcp/go-live.md +100 -100
- package/docs/mcp/overview.md +76 -76
- package/docs/mcp/setup.md +173 -173
- package/docs/mcp/shape.md +77 -77
- package/docs/mcp/tools.md +102 -102
- package/docs/plugins.md +24 -24
- package/docs/quickstart.md +72 -72
- package/docs/writing.mdx +72 -72
- package/package.json +1 -1
- package/src/app/ZuploBuildConfig.ts +36 -36
- package/src/app/apitogoBrandTheme.ts +47 -47
- package/src/app/defaultTheme.css +77 -77
- package/src/app/demo-cdn.html +37 -37
- package/src/app/demo.html +21 -21
- package/src/app/demo.tsx +77 -77
- package/src/app/entry.client.tsx +107 -107
- package/src/app/entry.server.tsx +160 -160
- package/src/app/env.ts +40 -40
- package/src/app/main.css +400 -400
- package/src/app/main.tsx +167 -167
- package/src/app/polyfills.ts +47 -47
- package/src/app/processRoutes.tsx +51 -51
- package/src/app/sentry.ts +24 -24
- package/src/app/standalone.html +23 -23
- package/src/app/standalone.tsx +60 -60
- package/src/app/utils/createRedirectRoutes.ts +11 -11
- package/src/config/apitogo-config-core.ts +7 -3
- package/src/config/apitogo-config-loader.browser.ts +17 -17
- package/src/config/apitogo-config-loader.node.ts +88 -88
- package/src/config/apitogo-config-loader.ts +6 -6
- package/src/config/build-apitogo-config.ts +61 -61
- package/src/config/config.ts +74 -74
- package/src/config/create-plugin.ts +71 -71
- package/src/config/default-landing-content.ts +149 -149
- package/src/config/file-exists.ts +6 -6
- package/src/config/gateway-limits.ts +128 -128
- package/src/config/landing-manifest.ts +11 -11
- package/src/config/landing-openapi-showcase.ts +124 -124
- package/src/config/loader.ts +375 -375
- package/src/config/local-manifest.ts +545 -545
- package/src/config/resolve-modules.ts +354 -354
- package/src/config/v2/__fixtures__/scenario-a-docs-only.json +6 -6
- package/src/config/v2/__fixtures__/scenario-b-managed-origin.json +14 -14
- package/src/config/v2/__fixtures__/scenario-c-proxy-oidc.json +15 -15
- package/src/config/v2/__fixtures__/scenario-h-full-stack.json +28 -28
- package/src/config/v2/docs-navigation.ts +72 -72
- package/src/config/v2/errors.ts +42 -42
- package/src/config/v2/gateway-reference.ts +220 -220
- package/src/config/v2/gateway-request.ts +123 -123
- package/src/config/v2/home-redirect.ts +98 -98
- package/src/config/v2/index.ts +126 -126
- package/src/config/v2/init.ts +273 -273
- package/src/config/v2/layout.ts +237 -237
- package/src/config/v2/ledger.ts +235 -235
- package/src/config/v2/link-directory.ts +34 -0
- package/src/config/v2/load.ts +70 -70
- package/src/config/v2/manifest.ts +452 -452
- package/src/config/v2/parse.ts +214 -214
- package/src/config/v2/paths.ts +54 -54
- package/src/config/v2/portal-profile.ts +88 -88
- package/src/config/v2/resolve.ts +188 -188
- package/src/config/v2/rules.ts +714 -714
- package/src/config/v2/schema.ts +245 -245
- package/src/config/v2/services.ts +663 -663
- package/src/config/v2/workspace.ts +3 -2
- package/src/config/v2/wrapper.ts +24 -2
- package/src/config/validators/BuildSchema.ts +95 -95
- package/src/config/validators/HeaderNavigationSchema.ts +44 -44
- package/src/config/validators/InputNavigationSchema.test-d.ts +178 -178
- package/src/config/validators/InputNavigationSchema.ts +206 -206
- package/src/config/validators/ModulesSchema.ts +502 -502
- package/src/config/validators/NavigationSchema.ts +352 -352
- package/src/config/validators/ProtectedRoutesSchema.ts +35 -35
- package/src/config/validators/ZudokuConfig.ts +792 -792
- package/src/config/validators/auth.ts +3 -3
- package/src/config/validators/reason-codes.ts +7 -7
- package/src/env.d.ts +11 -11
- package/src/index.ts +83 -83
- package/src/lib/MissingIcon.tsx +22 -22
- package/src/lib/assets/language-icons/bun.tsx +50 -50
- package/src/lib/assets/language-icons/c.tsx +31 -31
- package/src/lib/assets/language-icons/commonlisp.tsx +22 -22
- package/src/lib/assets/language-icons/cpp.tsx +35 -35
- package/src/lib/assets/language-icons/csharp.tsx +35 -35
- package/src/lib/assets/language-icons/css.tsx +36 -36
- package/src/lib/assets/language-icons/dart.tsx +39 -39
- package/src/lib/assets/language-icons/elixir.tsx +19 -19
- package/src/lib/assets/language-icons/go.tsx +19 -19
- package/src/lib/assets/language-icons/graphql.tsx +19 -19
- package/src/lib/assets/language-icons/html.tsx +24 -24
- package/src/lib/assets/language-icons/java.tsx +35 -35
- package/src/lib/assets/language-icons/javascript.tsx +11 -11
- package/src/lib/assets/language-icons/json.tsx +19 -19
- package/src/lib/assets/language-icons/kotlin.tsx +30 -30
- package/src/lib/assets/language-icons/markdown.tsx +19 -19
- package/src/lib/assets/language-icons/mdx.tsx +23 -23
- package/src/lib/assets/language-icons/npm.tsx +15 -15
- package/src/lib/assets/language-icons/objectivec.tsx +23 -23
- package/src/lib/assets/language-icons/ocaml.tsx +34 -34
- package/src/lib/assets/language-icons/php.tsx +19 -19
- package/src/lib/assets/language-icons/pnpm.tsx +32 -32
- package/src/lib/assets/language-icons/powershell.tsx +27 -27
- package/src/lib/assets/language-icons/python.tsx +23 -23
- package/src/lib/assets/language-icons/react.tsx +21 -21
- package/src/lib/assets/language-icons/ruby.tsx +19 -19
- package/src/lib/assets/language-icons/rust.tsx +19 -19
- package/src/lib/assets/language-icons/scala.tsx +19 -19
- package/src/lib/assets/language-icons/shell.tsx +19 -19
- package/src/lib/assets/language-icons/swift.tsx +19 -19
- package/src/lib/assets/language-icons/toml.tsx +23 -23
- package/src/lib/assets/language-icons/typescript.tsx +23 -23
- package/src/lib/assets/language-icons/xml.tsx +19 -19
- package/src/lib/assets/language-icons/yaml.tsx +23 -23
- package/src/lib/assets/language-icons/yarn.tsx +17 -17
- package/src/lib/assets/language-icons/zig.tsx +32 -32
- package/src/lib/auth/issuer.ts +34 -34
- package/src/lib/authentication/AuthenticationPlugin.tsx +33 -33
- package/src/lib/authentication/auth-header-nav.ts +46 -46
- package/src/lib/authentication/authentication.ts +51 -51
- package/src/lib/authentication/components/CallbackHandler.tsx +45 -45
- package/src/lib/authentication/components/OAuthErrorPage.tsx +197 -197
- package/src/lib/authentication/components/ProductionUnlockPage.tsx +26 -26
- package/src/lib/authentication/components/SignIn.tsx +46 -46
- package/src/lib/authentication/components/SignOut.tsx +15 -15
- package/src/lib/authentication/components/SignUp.tsx +48 -48
- package/src/lib/authentication/constants.ts +1 -1
- package/src/lib/authentication/errors.ts +34 -34
- package/src/lib/authentication/header-nav-filter.ts +35 -35
- package/src/lib/authentication/hook.ts +141 -141
- package/src/lib/authentication/providers/auth0.tsx +91 -91
- package/src/lib/authentication/providers/azureb2c.tsx +223 -223
- package/src/lib/authentication/providers/clerk.tsx +224 -224
- package/src/lib/authentication/providers/dev-portal-auth-pages.tsx +602 -602
- package/src/lib/authentication/providers/dev-portal-constants.ts +38 -38
- package/src/lib/authentication/providers/dev-portal-demo.ts +151 -151
- package/src/lib/authentication/providers/dev-portal-utils.ts +279 -279
- package/src/lib/authentication/providers/dev-portal.tsx +448 -448
- package/src/lib/authentication/providers/firebase.tsx +486 -486
- package/src/lib/authentication/providers/openid.tsx +516 -516
- package/src/lib/authentication/providers/supabase.tsx +412 -412
- package/src/lib/authentication/providers/util.ts +26 -26
- package/src/lib/authentication/state.ts +180 -180
- package/src/lib/authentication/ui/AuthCard.tsx +7 -7
- package/src/lib/authentication/ui/EmailLinkCallbackUi.tsx +129 -129
- package/src/lib/authentication/ui/EmailLinkSentUi.tsx +105 -105
- package/src/lib/authentication/ui/EmailLinkSignInUi.tsx +100 -100
- package/src/lib/authentication/ui/EmailVerificationUi.tsx +128 -128
- package/src/lib/authentication/ui/ZudokuAuthUi.tsx +620 -620
- package/src/lib/authentication/ui/icons/Apple.tsx +10 -10
- package/src/lib/authentication/ui/icons/Facebook.tsx +15 -15
- package/src/lib/authentication/ui/icons/Github.tsx +16 -16
- package/src/lib/authentication/ui/icons/Google.tsx +16 -16
- package/src/lib/authentication/ui/icons/Microsoft.tsx +12 -12
- package/src/lib/authentication/ui/icons/X.tsx +10 -10
- package/src/lib/authentication/utils/relativeRedirectUrl.ts +16 -16
- package/src/lib/components/AiAssistantMenuItems.tsx +102 -102
- package/src/lib/components/AnchorLink.tsx +24 -24
- package/src/lib/components/Autocomplete.tsx +123 -123
- package/src/lib/components/Banner.tsx +52 -52
- package/src/lib/components/Bootstrap.tsx +77 -77
- package/src/lib/components/BuildCheck.tsx +87 -87
- package/src/lib/components/CategoryHeading.tsx +19 -19
- package/src/lib/components/ClientOnly.tsx +19 -19
- package/src/lib/components/DeveloperHint.tsx +39 -39
- package/src/lib/components/ErrorPage.tsx +26 -26
- package/src/lib/components/Footer.tsx +146 -146
- package/src/lib/components/Framed.tsx +51 -51
- package/src/lib/components/Header.tsx +261 -261
- package/src/lib/components/HeaderAuthActions.tsx +32 -32
- package/src/lib/components/HeaderNavigation.tsx +176 -176
- package/src/lib/components/Heading.tsx +84 -84
- package/src/lib/components/InlineCode.tsx +15 -15
- package/src/lib/components/LandingLayout.tsx +27 -27
- package/src/lib/components/LanguageIcon.tsx +199 -199
- package/src/lib/components/Layout.tsx +59 -59
- package/src/lib/components/Main.tsx +52 -52
- package/src/lib/components/Markdown.tsx +55 -55
- package/src/lib/components/Mermaid.tsx +74 -74
- package/src/lib/components/Meta.tsx +46 -46
- package/src/lib/components/MobileTopNavigation.tsx +283 -283
- package/src/lib/components/MultiSelect.tsx +86 -86
- package/src/lib/components/NotFoundPage.tsx +37 -37
- package/src/lib/components/PageProgress.tsx +28 -28
- package/src/lib/components/PagefindSearchMeta.tsx +14 -14
- package/src/lib/components/Pagination.tsx +44 -44
- package/src/lib/components/PathRenderer.tsx +61 -61
- package/src/lib/components/PluginHeads.tsx +17 -17
- package/src/lib/components/Search.tsx +70 -70
- package/src/lib/components/SiteTitle.tsx +58 -58
- package/src/lib/components/Slot.tsx +65 -65
- package/src/lib/components/Spinner.tsx +5 -5
- package/src/lib/components/StatusPage.tsx +96 -96
- package/src/lib/components/ThemeSwitch.tsx +99 -99
- package/src/lib/components/TopNavigation.tsx +90 -90
- package/src/lib/components/Typography.tsx +12 -12
- package/src/lib/components/Zudoku.tsx +105 -105
- package/src/lib/components/cache.ts +27 -27
- package/src/lib/components/context/RenderContext.ts +11 -11
- package/src/lib/components/context/RouterEventsEmitter.tsx +19 -19
- package/src/lib/components/context/SlotProvider.tsx +149 -149
- package/src/lib/components/context/ViewportAnchorContext.tsx +137 -137
- package/src/lib/components/context/ZudokuContext.ts +140 -140
- package/src/lib/components/context/ZudokuProvider.tsx +20 -20
- package/src/lib/components/context/ZudokuReactContext.tsx +17 -17
- package/src/lib/components/index.ts +36 -36
- package/src/lib/components/navigation/Navigation.tsx +51 -51
- package/src/lib/components/navigation/NavigationBadge.tsx +48 -48
- package/src/lib/components/navigation/NavigationCategory.tsx +157 -157
- package/src/lib/components/navigation/NavigationFilterContext.tsx +28 -28
- package/src/lib/components/navigation/NavigationFilterInput.tsx +35 -35
- package/src/lib/components/navigation/NavigationItem.tsx +181 -181
- package/src/lib/components/navigation/NavigationWrapper.tsx +49 -49
- package/src/lib/components/navigation/PoweredByZudoku.tsx +39 -39
- package/src/lib/components/navigation/Toc.tsx +137 -137
- package/src/lib/components/navigation/ZudokuLogo.tsx +25 -25
- package/src/lib/components/navigation/ZuploLogo.tsx +14 -14
- package/src/lib/components/navigation/utils.ts +234 -234
- package/src/lib/core/RouteGuard.tsx +260 -260
- package/src/lib/core/ZudokuContext.ts +272 -272
- package/src/lib/core/plugins.ts +139 -139
- package/src/lib/core/react-query.ts +1 -1
- package/src/lib/core/router.ts +1 -1
- package/src/lib/core/transform-config.ts +67 -67
- package/src/lib/demo/DemoAnnouncement.tsx +17 -17
- package/src/lib/errors/ErrorAlert.tsx +28 -28
- package/src/lib/errors/ErrorMessage.tsx +50 -50
- package/src/lib/errors/RouterError.tsx +18 -18
- package/src/lib/errors/ServerError.tsx +5 -5
- package/src/lib/errors/TopLevelError.tsx +6 -6
- package/src/lib/hooks/index.ts +13 -13
- package/src/lib/hooks/useEvent.ts +41 -41
- package/src/lib/hooks/useHighlighter.ts +15 -15
- package/src/lib/hooks/useHotkey.ts +70 -70
- package/src/lib/icons.ts +2 -2
- package/src/lib/navigation/applyRules.ts +127 -127
- package/src/lib/navigation/pathMatcher.ts +81 -81
- package/src/lib/oas/graphql/circular.ts +69 -69
- package/src/lib/oas/graphql/constants.ts +2 -2
- package/src/lib/oas/graphql/index.ts +791 -791
- package/src/lib/oas/parser/dereference/index.ts +84 -84
- package/src/lib/oas/parser/dereference/resolveRef.ts +32 -32
- package/src/lib/oas/parser/index.ts +129 -129
- package/src/lib/oas/parser/schemas/v3.0.json +1489 -1489
- package/src/lib/oas/parser/schemas/v3.1.json +1298 -1298
- package/src/lib/oas/parser/upgrade/index.ts +174 -174
- package/src/lib/plugins/api-catalog/Catalog.tsx +80 -80
- package/src/lib/plugins/api-catalog/index.tsx +119 -119
- package/src/lib/plugins/api-keys/CreateApiKey.tsx +128 -128
- package/src/lib/plugins/api-keys/CreateApiKeyDialog.tsx +49 -49
- package/src/lib/plugins/api-keys/ProtectedRoute.tsx +33 -33
- package/src/lib/plugins/api-keys/SettingsApiKeys.tsx +72 -72
- package/src/lib/plugins/api-keys/index.tsx +274 -274
- package/src/lib/plugins/api-keys/settings/ApiKeyItem.tsx +342 -342
- package/src/lib/plugins/api-keys/settings/ApiKeyList.tsx +67 -67
- package/src/lib/plugins/api-keys/settings/RevealApiKey.tsx +127 -127
- package/src/lib/plugins/custom-pages/index.tsx +32 -32
- package/src/lib/plugins/markdown/MdxPage.tsx +260 -260
- package/src/lib/plugins/markdown/assets/ChatGPTLogo.tsx +11 -11
- package/src/lib/plugins/markdown/assets/ClaudeLogo.tsx +19 -19
- package/src/lib/plugins/markdown/index.tsx +82 -82
- package/src/lib/plugins/openapi/ApiHeader.tsx +96 -96
- package/src/lib/plugins/openapi/CollapsibleCode.tsx +88 -88
- package/src/lib/plugins/openapi/ColorizedParam.tsx +115 -115
- package/src/lib/plugins/openapi/DownloadSchemaButton.tsx +94 -94
- package/src/lib/plugins/openapi/Endpoint.tsx +85 -85
- package/src/lib/plugins/openapi/GeneratedExampleSidecarBox.tsx +52 -52
- package/src/lib/plugins/openapi/MCPEndpoint.tsx +273 -273
- package/src/lib/plugins/openapi/OasProvider.tsx +74 -74
- package/src/lib/plugins/openapi/OperationList.tsx +275 -275
- package/src/lib/plugins/openapi/OperationListItem.tsx +183 -183
- package/src/lib/plugins/openapi/ParamInfos.tsx +96 -96
- package/src/lib/plugins/openapi/ParameterList.tsx +53 -53
- package/src/lib/plugins/openapi/ParameterListItem.tsx +161 -161
- package/src/lib/plugins/openapi/PlaygroundDialogWrapper.tsx +67 -67
- package/src/lib/plugins/openapi/RequestBodySidecarBox.tsx +63 -63
- package/src/lib/plugins/openapi/ResponsesSidecarBox.tsx +120 -120
- package/src/lib/plugins/openapi/SchemaInfo.tsx +344 -344
- package/src/lib/plugins/openapi/SchemaList.tsx +113 -113
- package/src/lib/plugins/openapi/Sidecar.tsx +314 -314
- package/src/lib/plugins/openapi/SidecarBox.tsx +57 -57
- package/src/lib/plugins/openapi/SidecarExamples.tsx +177 -177
- package/src/lib/plugins/openapi/SimpleSelect.tsx +46 -46
- package/src/lib/plugins/openapi/client/GraphQLClient.tsx +73 -73
- package/src/lib/plugins/openapi/client/GraphQLContext.tsx +16 -16
- package/src/lib/plugins/openapi/client/createServer.ts +35 -35
- package/src/lib/plugins/openapi/client/useCreateQuery.ts +44 -44
- package/src/lib/plugins/openapi/components/ConstValue.tsx +24 -24
- package/src/lib/plugins/openapi/components/EnumValues.tsx +58 -58
- package/src/lib/plugins/openapi/components/NonHighlightedCode.tsx +22 -22
- package/src/lib/plugins/openapi/components/ResponseContent.tsx +117 -117
- package/src/lib/plugins/openapi/components/SelectOnClick.tsx +29 -29
- package/src/lib/plugins/openapi/context.tsx +16 -16
- package/src/lib/plugins/openapi/graphql/fragment-masking.ts +104 -104
- package/src/lib/plugins/openapi/graphql/gql.ts +95 -95
- package/src/lib/plugins/openapi/graphql/graphql.ts +835 -835
- package/src/lib/plugins/openapi/graphql/index.ts +2 -2
- package/src/lib/plugins/openapi/index.tsx +221 -221
- package/src/lib/plugins/openapi/interfaces.ts +100 -100
- package/src/lib/plugins/openapi/playground/BodyPanel.tsx +274 -274
- package/src/lib/plugins/openapi/playground/CollapsibleHeader.tsx +51 -51
- package/src/lib/plugins/openapi/playground/ExamplesDropdown.tsx +60 -60
- package/src/lib/plugins/openapi/playground/Headers.tsx +194 -194
- package/src/lib/plugins/openapi/playground/IdentityDialog.tsx +75 -75
- package/src/lib/plugins/openapi/playground/IdentitySelector.tsx +38 -38
- package/src/lib/plugins/openapi/playground/ParamsGrid.tsx +45 -45
- package/src/lib/plugins/openapi/playground/PathParams.tsx +53 -53
- package/src/lib/plugins/openapi/playground/Playground.tsx +602 -602
- package/src/lib/plugins/openapi/playground/PlaygroundDialog.tsx +62 -62
- package/src/lib/plugins/openapi/playground/QueryParams.tsx +151 -151
- package/src/lib/plugins/openapi/playground/RequestLoginDialog.tsx +70 -70
- package/src/lib/plugins/openapi/playground/Spinner.tsx +87 -87
- package/src/lib/plugins/openapi/playground/createUrl.ts +25 -25
- package/src/lib/plugins/openapi/playground/fileUtils.ts +36 -36
- package/src/lib/plugins/openapi/playground/rememberedIdentity.ts +26 -26
- package/src/lib/plugins/openapi/playground/request-panel/MultipartField.tsx +91 -91
- package/src/lib/plugins/openapi/playground/request-panel/UrlPath.tsx +31 -31
- package/src/lib/plugins/openapi/playground/request-panel/UrlQueryParams.tsx +29 -29
- package/src/lib/plugins/openapi/playground/request-panel/useKeyValueFieldManager.ts +340 -340
- package/src/lib/plugins/openapi/playground/result-panel/AudioPlayer.tsx +50 -50
- package/src/lib/plugins/openapi/playground/result-panel/ResponseStatusBar.tsx +117 -117
- package/src/lib/plugins/openapi/playground/result-panel/ResponseTab.tsx +355 -355
- package/src/lib/plugins/openapi/playground/result-panel/ResultPanel.tsx +106 -106
- package/src/lib/plugins/openapi/playground/result-panel/convertToTypes.ts +36 -36
- package/src/lib/plugins/openapi/playground/serializeQueryParams.ts +125 -125
- package/src/lib/plugins/openapi/playground/useRememberSkipLoginDialog.tsx +23 -23
- package/src/lib/plugins/openapi/processors/injectApitogoGatewayServer.ts +36 -36
- package/src/lib/plugins/openapi/processors/removeExtensions.ts +29 -29
- package/src/lib/plugins/openapi/processors/removeParameters.ts +103 -103
- package/src/lib/plugins/openapi/processors/removePaths.ts +57 -57
- package/src/lib/plugins/openapi/processors/traverse.ts +1 -1
- package/src/lib/plugins/openapi/schema/SchemaExampleAndDefault.tsx +39 -39
- package/src/lib/plugins/openapi/schema/SchemaPropertyItem.tsx +189 -189
- package/src/lib/plugins/openapi/schema/SchemaView.tsx +299 -299
- package/src/lib/plugins/openapi/schema/UnionView.tsx +132 -132
- package/src/lib/plugins/openapi/schema/union-helpers.ts +123 -123
- package/src/lib/plugins/openapi/schema/utils.ts +49 -49
- package/src/lib/plugins/openapi/state.ts +36 -36
- package/src/lib/plugins/openapi/util/buildTagCategories.ts +54 -54
- package/src/lib/plugins/openapi/util/createHttpSnippet.ts +135 -135
- package/src/lib/plugins/openapi/util/createNavigationCategory.tsx +39 -39
- package/src/lib/plugins/openapi/util/generateSchemaExample.ts +191 -191
- package/src/lib/plugins/openapi/util/getRoutes.tsx +265 -265
- package/src/lib/plugins/openapi/util/methodColorMap.tsx +11 -11
- package/src/lib/plugins/openapi/util/methodToColor.ts +27 -27
- package/src/lib/plugins/openapi/util/sanitizeMarkdownForMetatag.tsx +32 -32
- package/src/lib/plugins/openapi/util/useWarmupSchema.ts +23 -23
- package/src/lib/plugins/search-inkeep/index.tsx +108 -108
- package/src/lib/plugins/search-inkeep/inkeep.ts +24 -24
- package/src/lib/plugins/search-pagefind/IndexingDialog.tsx +162 -162
- package/src/lib/plugins/search-pagefind/PagefindSearch.tsx +202 -202
- package/src/lib/plugins/search-pagefind/ResultList.tsx +122 -122
- package/src/lib/plugins/search-pagefind/get-results.tsx +74 -74
- package/src/lib/plugins/search-pagefind/index.tsx +21 -21
- package/src/lib/plugins/search-pagefind/types.ts +118 -118
- package/src/lib/shiki-constants.ts +24 -24
- package/src/lib/shiki.ts +179 -179
- package/src/lib/testing/index.tsx +147 -147
- package/src/lib/ui/Accordion.tsx +55 -55
- package/src/lib/ui/ActionButton.tsx +21 -21
- package/src/lib/ui/Alert.tsx +84 -84
- package/src/lib/ui/AlertDialog.tsx +195 -195
- package/src/lib/ui/AspectRatio.tsx +5 -5
- package/src/lib/ui/Badge.tsx +48 -48
- package/src/lib/ui/Breadcrumb.tsx +115 -115
- package/src/lib/ui/Button.tsx +65 -65
- package/src/lib/ui/ButtonGroup.tsx +80 -80
- package/src/lib/ui/Callout.tsx +113 -113
- package/src/lib/ui/Card.tsx +95 -95
- package/src/lib/ui/Carousel.tsx +259 -259
- package/src/lib/ui/Checkbox.tsx +28 -28
- package/src/lib/ui/CodeBlock.tsx +92 -92
- package/src/lib/ui/CodeTabPanel.tsx +13 -13
- package/src/lib/ui/CodeTabs.tsx +161 -161
- package/src/lib/ui/Collapsible.tsx +31 -31
- package/src/lib/ui/Command.tsx +191 -191
- package/src/lib/ui/Dialog.tsx +138 -138
- package/src/lib/ui/DismissibleAlert.tsx +74 -74
- package/src/lib/ui/Drawer.tsx +121 -121
- package/src/lib/ui/DropdownMenu.tsx +254 -254
- package/src/lib/ui/EmbeddedCodeBlock.tsx +99 -99
- package/src/lib/ui/Form.tsx +176 -176
- package/src/lib/ui/Frame.tsx +81 -81
- package/src/lib/ui/HoverCard.tsx +26 -26
- package/src/lib/ui/Input.tsx +23 -23
- package/src/lib/ui/InputGroup.tsx +168 -168
- package/src/lib/ui/Item.tsx +213 -213
- package/src/lib/ui/Kbd.tsx +28 -28
- package/src/lib/ui/Label.tsx +23 -23
- package/src/lib/ui/NativeSelect.tsx +48 -48
- package/src/lib/ui/NavigationMenu.tsx +169 -169
- package/src/lib/ui/Pagination.tsx +115 -115
- package/src/lib/ui/Popover.tsx +28 -28
- package/src/lib/ui/Progress.tsx +25 -25
- package/src/lib/ui/RadioGroup.tsx +41 -41
- package/src/lib/ui/ReactComponentDoc.tsx +68 -68
- package/src/lib/ui/ScrollArea.tsx +45 -45
- package/src/lib/ui/Secret.tsx +133 -133
- package/src/lib/ui/Select.tsx +184 -184
- package/src/lib/ui/Separator.tsx +25 -25
- package/src/lib/ui/Skeleton.tsx +15 -15
- package/src/lib/ui/Slider.tsx +25 -25
- package/src/lib/ui/Stepper.tsx +8 -8
- package/src/lib/ui/Switch.tsx +26 -26
- package/src/lib/ui/SyntaxHighlight.tsx +54 -54
- package/src/lib/ui/Tabs.tsx +52 -52
- package/src/lib/ui/Textarea.tsx +22 -22
- package/src/lib/ui/Toggle.tsx +42 -42
- package/src/lib/ui/ToggleGroup.tsx +58 -58
- package/src/lib/ui/Tooltip.tsx +65 -65
- package/src/lib/ui/Value.tsx +42 -42
- package/src/lib/ui/util.tsx +8 -8
- package/src/lib/util/MdxComponents.tsx +150 -150
- package/src/lib/util/cn.ts +6 -6
- package/src/lib/util/createVariantComponent.tsx +61 -61
- package/src/lib/util/ensureArray.ts +3 -3
- package/src/lib/util/flattenAllOf.ts +72 -72
- package/src/lib/util/flattenAllOfProcessor.ts +74 -74
- package/src/lib/util/hastToJsx.tsx +67 -67
- package/src/lib/util/humanFileSize.ts +15 -15
- package/src/lib/util/invariant.ts +52 -52
- package/src/lib/util/joinUrl.ts +60 -60
- package/src/lib/util/logInit.ts +9 -9
- package/src/lib/util/objectEntries.ts +5 -5
- package/src/lib/util/os.ts +18 -18
- package/src/lib/util/pastellize.ts +25 -25
- package/src/lib/util/problemJson.ts +63 -63
- package/src/lib/util/readFrontmatter.ts +14 -14
- package/src/lib/util/renderIf.ts +4 -4
- package/src/lib/util/scrollIntoViewIfNeeded.ts +18 -18
- package/src/lib/util/slugify.ts +21 -21
- package/src/lib/util/syncZustandState.ts +22 -22
- package/src/lib/util/traverse.ts +70 -70
- package/src/lib/util/types.ts +7 -7
- package/src/lib/util/url.ts +18 -18
- package/src/lib/util/useCopyToClipboard.ts +17 -17
- package/src/lib/util/useExposedProps.tsx +27 -27
- package/src/lib/util/useIsomorphicLayoutEffect.ts +6 -6
- package/src/lib/util/useLatest.ts +18 -18
- package/src/lib/util/useOnScreen.ts +34 -34
- package/src/lib/util/useScrollToAnchor.ts +75 -75
- package/src/lib/util/useScrollToTop.ts +18 -18
- package/src/types.d.ts +66 -66
- package/src/vite/api/SchemaManager.ts +371 -371
- package/src/vite/api/schema-codegen.ts +163 -163
- package/src/vite/build.ts +237 -237
- package/src/vite/config.ts +273 -273
- package/src/vite/css/collect.ts +42 -42
- package/src/vite/css/plugin.ts +105 -105
- package/src/vite/debug.ts +18 -18
- package/src/vite/dev-portal-env.ts +29 -29
- package/src/vite/dev-server.ts +294 -294
- package/src/vite/html.ts +52 -52
- package/src/vite/index.ts +2 -2
- package/src/vite/llms.ts +99 -99
- package/src/vite/mdx/rehype-extract-toc-with-jsx-export.ts +19 -19
- package/src/vite/mdx/rehype-extract-toc-with-jsx.ts +70 -70
- package/src/vite/mdx/remark-code-tabs.ts +47 -47
- package/src/vite/mdx/remark-inject-filepath.ts +13 -13
- package/src/vite/mdx/remark-last-modified.ts +103 -103
- package/src/vite/mdx/remark-link-rewrite.ts +23 -23
- package/src/vite/mdx/remark-normalize-image-url.ts +15 -15
- package/src/vite/mdx/remark-static-generation.ts +149 -149
- package/src/vite/mdx/utils.ts +32 -32
- package/src/vite/output.ts +261 -261
- package/src/vite/package-root.ts +12 -12
- package/src/vite/pagefind-dev-index.ts +100 -100
- package/src/vite/pagefind-dev-stub.ts +17 -17
- package/src/vite/plugin-api-keys.ts +55 -55
- package/src/vite/plugin-api.ts +328 -328
- package/src/vite/plugin-auth.ts +41 -41
- package/src/vite/plugin-component.ts +45 -45
- package/src/vite/plugin-config-reload.ts +47 -47
- package/src/vite/plugin-config.ts +131 -131
- package/src/vite/plugin-custom-pages.ts +36 -36
- package/src/vite/plugin-doc-metadata.ts +45 -45
- package/src/vite/plugin-docs.ts +205 -205
- package/src/vite/plugin-local-manifest.ts +100 -100
- package/src/vite/plugin-markdown-export.ts +214 -214
- package/src/vite/plugin-mdx.ts +149 -149
- package/src/vite/plugin-metadata.ts +30 -30
- package/src/vite/plugin-modules.ts +188 -188
- package/src/vite/plugin-navigation.ts +106 -106
- package/src/vite/plugin-offroot-resolve.ts +67 -67
- package/src/vite/plugin-search.ts +59 -59
- package/src/vite/plugin-shiki-register.ts +97 -97
- package/src/vite/plugin-theme.ts +458 -458
- package/src/vite/plugin.ts +48 -48
- package/src/vite/prerender/prerender.ts +242 -242
- package/src/vite/prerender/utils.ts +76 -76
- package/src/vite/prerender/worker.ts +118 -118
- package/src/vite/reporter.ts +34 -34
- package/src/vite/shadcn-registry.ts +48 -48
- package/src/vite/sitemap.ts +78 -78
- package/src/vite/ssr-templates/cloudflare.ts +19 -19
- package/src/vite/ssr-templates/node.ts +25 -25
- package/src/vite/ssr-templates/vercel.ts +22 -22
- package/src/vite/watch-ignore.ts +70 -70
- package/src/vite/zuplo.ts +30 -30
- package/src/vite-env.d.ts +6 -6
- package/src/zuplo/enrich-with-zuplo-mcp.ts +168 -168
- package/src/zuplo/enrich-with-zuplo.ts +256 -256
- package/src/zuplo/policy-types.ts +46 -46
- package/src/zuplo/with-zuplo-processors.ts +36 -36
- package/src/zuplo/with-zuplo.ts +14 -14
- package/dist/declarations/cli/platform/client/deploy-server.d.ts +0 -16
package/src/vite/plugin-theme.ts
CHANGED
|
@@ -1,458 +1,458 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import { normalizeTheme, type ThemeRegistration } from "shiki";
|
|
4
|
-
import type { Plugin } from "vite";
|
|
5
|
-
import type { ConfigWithMeta } from "../config/loader.js";
|
|
6
|
-
import { getCurrentConfig } from "../config/loader.js";
|
|
7
|
-
import type {
|
|
8
|
-
FontConfig,
|
|
9
|
-
ZudokuConfig,
|
|
10
|
-
} from "../config/validators/ZudokuConfig.js";
|
|
11
|
-
import { defaultHighlightOptions } from "../lib/shiki.js";
|
|
12
|
-
import { objectEntries } from "../lib/util/objectEntries.js";
|
|
13
|
-
import {
|
|
14
|
-
fetchShadcnRegistryItem,
|
|
15
|
-
type RegistryItemCss,
|
|
16
|
-
} from "./shadcn-registry.js";
|
|
17
|
-
|
|
18
|
-
// prettier-ignore
|
|
19
|
-
const THEME_VARIABLES = [
|
|
20
|
-
"background", "foreground", "card", "cardForeground", "popover", "popoverForeground", "primary",
|
|
21
|
-
"primaryForeground", "secondary", "secondaryForeground", "muted", "mutedForeground", "accent",
|
|
22
|
-
"accentForeground", "destructive", "destructiveForeground", "border", "input", "ring", "radius",
|
|
23
|
-
] as const;
|
|
24
|
-
|
|
25
|
-
const uncamelize = (str: string) =>
|
|
26
|
-
str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
|
|
27
|
-
|
|
28
|
-
export type Theme = Partial<Record<(typeof THEME_VARIABLES)[number], string>>;
|
|
29
|
-
|
|
30
|
-
const processColorValue = (value: string | undefined): string => {
|
|
31
|
-
if (!value) return "";
|
|
32
|
-
const color =
|
|
33
|
-
!value.startsWith("#") &&
|
|
34
|
-
!value.includes("(") &&
|
|
35
|
-
value.split(" ").length >= 3
|
|
36
|
-
? // Assume legacy tailwind hsl format
|
|
37
|
-
`hsl(${value})`
|
|
38
|
-
: value;
|
|
39
|
-
|
|
40
|
-
return color;
|
|
41
|
-
};
|
|
42
|
-
|
|
43
|
-
const generateCss = (theme: Theme) =>
|
|
44
|
-
objectEntries(theme)
|
|
45
|
-
.filter(([key]) => THEME_VARIABLES.includes(key))
|
|
46
|
-
.map(([key, value]) => `--${uncamelize(key)}: ${processColorValue(value)};`)
|
|
47
|
-
.join("\n");
|
|
48
|
-
|
|
49
|
-
const generateRegistryCss = (cssVars: Record<string, string>) =>
|
|
50
|
-
Object.entries(cssVars)
|
|
51
|
-
.map(([key, value]) => ` --${key}: ${value};`)
|
|
52
|
-
.join("\n");
|
|
53
|
-
|
|
54
|
-
const processCustomCss = (css: string | RegistryItemCss): string => {
|
|
55
|
-
if (typeof css === "string") {
|
|
56
|
-
return css;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const processStyles = (
|
|
60
|
-
styles: RegistryItemCss | string,
|
|
61
|
-
indent = "",
|
|
62
|
-
): string[] => {
|
|
63
|
-
if (typeof styles === "string") {
|
|
64
|
-
return [`${indent}${styles}`];
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
const rules = [];
|
|
68
|
-
for (const [key, value] of Object.entries(styles)) {
|
|
69
|
-
if (typeof value === "string") {
|
|
70
|
-
rules.push(`${indent}${key}: ${value};`);
|
|
71
|
-
} else {
|
|
72
|
-
rules.push(`${indent}${key} {`);
|
|
73
|
-
rules.push(...processStyles(value, `${indent} `));
|
|
74
|
-
rules.push(`${indent}}`);
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
return rules;
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
return processStyles(css).join("\n");
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
const MAIN_REPLACE = "/* @vite-plugin-inject main */";
|
|
84
|
-
const DEFAULT_THEME_REPLACE = "/* @vite-plugin-inject defaultTheme */";
|
|
85
|
-
|
|
86
|
-
// prettier-ignore
|
|
87
|
-
export const GOOGLE_FONTS = [
|
|
88
|
-
"Inter", "Roboto", "Open Sans", "Poppins", "Montserrat", "Outfit",
|
|
89
|
-
"Plus Jakarta Sans", "DM Sans", "IBM Plex Sans", "Geist", "Oxanium",
|
|
90
|
-
"Architects Daughter", "Merriweather", "Playfair Display", "Lora",
|
|
91
|
-
"Source Serif Pro", "Libre Baskerville", "Space Grotesk", "JetBrains Mono",
|
|
92
|
-
"Fira Code", "Source Code Pro", "IBM Plex Mono", "Roboto Mono", "Space Mono", "Geist Mono",
|
|
93
|
-
] as const;
|
|
94
|
-
type GoogleFont = (typeof GOOGLE_FONTS)[number];
|
|
95
|
-
|
|
96
|
-
const getGoogleFontUrl = (font: string) =>
|
|
97
|
-
`https://fonts.googleapis.com/css2?family=${font.replaceAll(/\s+/g, "+")}:wght@400;500;600;700&display=swap`;
|
|
98
|
-
|
|
99
|
-
const processFont = (fontValue: string) => {
|
|
100
|
-
const families = fontValue
|
|
101
|
-
.split(",")
|
|
102
|
-
.map((f) => f.trim().replaceAll(/['"]/g, ""));
|
|
103
|
-
|
|
104
|
-
return families
|
|
105
|
-
.filter((family) => GOOGLE_FONTS.includes(family as GoogleFont))
|
|
106
|
-
.map((family) => getGoogleFontUrl(family));
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
const processFontConfig = (fontConfig?: FontConfig) => {
|
|
110
|
-
if (!fontConfig) return { url: undefined, fontFamily: undefined };
|
|
111
|
-
|
|
112
|
-
if (typeof fontConfig === "string") {
|
|
113
|
-
if (GOOGLE_FONTS.includes(fontConfig as GoogleFont)) {
|
|
114
|
-
return {
|
|
115
|
-
url: getGoogleFontUrl(fontConfig),
|
|
116
|
-
fontFamily: fontConfig,
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
return { url: undefined, fontFamily: fontConfig };
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
return {
|
|
123
|
-
url: fontConfig.url,
|
|
124
|
-
fontFamily: fontConfig.fontFamily,
|
|
125
|
-
};
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
/**
|
|
129
|
-
* The default fonts ship inside the portal (Latin subset, four weights) so a first paint makes no third-party
|
|
130
|
-
* request. Fonts a tenant picks by name still load from Google Fonts: that is their explicit choice.
|
|
131
|
-
*/
|
|
132
|
-
const bundledFont = (pkg: string) => {
|
|
133
|
-
// Absolute paths: the CSS is compiled from the site root, which does not see this package's dependencies.
|
|
134
|
-
const dir = path.dirname(
|
|
135
|
-
createRequire(import.meta.url).resolve(`@fontsource/${pkg}/package.json`),
|
|
136
|
-
);
|
|
137
|
-
return [400, 500, 600, 700].map((weight) =>
|
|
138
|
-
path.join(dir, `latin-${weight}.css`).split(path.sep).join("/"),
|
|
139
|
-
);
|
|
140
|
-
};
|
|
141
|
-
|
|
142
|
-
const DEFAULT_FONT_SANS = '"IBM Plex Sans", system-ui, sans-serif';
|
|
143
|
-
const DEFAULT_FONT_MONO = '"JetBrains Mono", monospace';
|
|
144
|
-
const DEFAULT_FONT_HEADING = '"Space Grotesk", system-ui, sans-serif';
|
|
145
|
-
|
|
146
|
-
const processFonts = async (themeConfig: ZudokuConfig["theme"]) => {
|
|
147
|
-
const imports: string[] = [];
|
|
148
|
-
const bundled: string[] = [];
|
|
149
|
-
const families = {
|
|
150
|
-
sans: DEFAULT_FONT_SANS,
|
|
151
|
-
serif: undefined as string | undefined,
|
|
152
|
-
mono: DEFAULT_FONT_MONO,
|
|
153
|
-
heading: DEFAULT_FONT_HEADING,
|
|
154
|
-
};
|
|
155
|
-
|
|
156
|
-
// Process user fonts
|
|
157
|
-
const userFonts = {
|
|
158
|
-
sans: processFontConfig(themeConfig?.fonts?.sans),
|
|
159
|
-
serif: processFontConfig(themeConfig?.fonts?.serif),
|
|
160
|
-
mono: processFontConfig(themeConfig?.fonts?.mono),
|
|
161
|
-
};
|
|
162
|
-
|
|
163
|
-
// Add user font imports
|
|
164
|
-
for (const font of Object.values(userFonts)) {
|
|
165
|
-
if (font.url) {
|
|
166
|
-
imports.push(font.url);
|
|
167
|
-
}
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
// Process registry fonts if available
|
|
171
|
-
let registryFonts = {
|
|
172
|
-
sans: undefined as string | undefined,
|
|
173
|
-
serif: undefined as string | undefined,
|
|
174
|
-
mono: undefined as string | undefined,
|
|
175
|
-
};
|
|
176
|
-
|
|
177
|
-
if (themeConfig?.registryUrl) {
|
|
178
|
-
try {
|
|
179
|
-
const registryItem = await fetchShadcnRegistryItem(
|
|
180
|
-
themeConfig.registryUrl,
|
|
181
|
-
);
|
|
182
|
-
const { cssVars = {} } = registryItem;
|
|
183
|
-
const { theme: regThemeVars = {}, light: regLightVars = {} } = cssVars;
|
|
184
|
-
|
|
185
|
-
registryFonts = {
|
|
186
|
-
sans: regLightVars["font-sans"] ?? regThemeVars["font-sans"],
|
|
187
|
-
serif: regLightVars["font-serif"] ?? regThemeVars["font-serif"],
|
|
188
|
-
mono: regLightVars["font-mono"] ?? regThemeVars["font-mono"],
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
// Add registry font imports
|
|
192
|
-
for (const font of Object.values(registryFonts)) {
|
|
193
|
-
if (font) {
|
|
194
|
-
imports.push(...processFont(font));
|
|
195
|
-
}
|
|
196
|
-
}
|
|
197
|
-
} catch {
|
|
198
|
-
// Registry fonts failed to load, continue with user fonts and defaults
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
// Determine final font families with priority: user > registry > defaults
|
|
203
|
-
families.sans =
|
|
204
|
-
userFonts.sans.fontFamily || registryFonts.sans || DEFAULT_FONT_SANS;
|
|
205
|
-
families.serif = userFonts.serif.fontFamily || registryFonts.serif;
|
|
206
|
-
families.mono =
|
|
207
|
-
userFonts.mono.fontFamily || registryFonts.mono || DEFAULT_FONT_MONO;
|
|
208
|
-
|
|
209
|
-
// Add default font imports if no user or registry fonts
|
|
210
|
-
if (!userFonts.sans.fontFamily && !registryFonts.sans) {
|
|
211
|
-
bundled.push(...bundledFont("ibm-plex-sans"));
|
|
212
|
-
}
|
|
213
|
-
if (!userFonts.mono.fontFamily && !registryFonts.mono) {
|
|
214
|
-
bundled.push(...bundledFont("jetbrains-mono"));
|
|
215
|
-
}
|
|
216
|
-
bundled.push(...bundledFont("space-grotesk"));
|
|
217
|
-
|
|
218
|
-
return { imports, bundled, families };
|
|
219
|
-
};
|
|
220
|
-
|
|
221
|
-
const MODULE_SOURCE_PACKAGES = [
|
|
222
|
-
"@lukoweb/apitogo-module-landing",
|
|
223
|
-
"@lukoweb/apitogo-module-user-panel",
|
|
224
|
-
];
|
|
225
|
-
|
|
226
|
-
/**
|
|
227
|
-
* Directories of module packages (landing, user panel) that use Tailwind
|
|
228
|
-
* utility classes. They live outside the Tailwind `source("..")` root set in
|
|
229
|
-
* main.css, so they must be added as explicit `@source` scan paths — otherwise
|
|
230
|
-
* their arbitrary-value utilities (e.g. the landing hero grid template and
|
|
231
|
-
* terminal card colors) are missing from the generated CSS.
|
|
232
|
-
*/
|
|
233
|
-
const resolveModuleSourceDirs = (config: ConfigWithMeta): string[] => {
|
|
234
|
-
if (config.__meta.mode === "internal") {
|
|
235
|
-
return MODULE_SOURCE_PACKAGES.map((pkg) =>
|
|
236
|
-
path.join(
|
|
237
|
-
config.__meta.moduleDir,
|
|
238
|
-
"..",
|
|
239
|
-
pkg.replace("@lukoweb/apitogo-", ""),
|
|
240
|
-
"src",
|
|
241
|
-
),
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
const require = createRequire(
|
|
246
|
-
path.join(config.__meta.rootDir, "package.json"),
|
|
247
|
-
);
|
|
248
|
-
const dirs: string[] = [];
|
|
249
|
-
for (const pkg of MODULE_SOURCE_PACKAGES) {
|
|
250
|
-
try {
|
|
251
|
-
dirs.push(path.dirname(require.resolve(pkg)));
|
|
252
|
-
} catch {
|
|
253
|
-
// Module package not installed — nothing to scan.
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
return dirs;
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
export const virtualModuleId = "virtual:zudoku-theme.css";
|
|
260
|
-
export const resolvedVirtualModuleId = `\0${virtualModuleId}`;
|
|
261
|
-
|
|
262
|
-
export const viteThemePlugin = (): Plugin => {
|
|
263
|
-
return {
|
|
264
|
-
// It is important to run before the Tailwind plugin
|
|
265
|
-
enforce: "pre",
|
|
266
|
-
name: "zudoku-css-theme",
|
|
267
|
-
|
|
268
|
-
resolveId(id) {
|
|
269
|
-
if (id === "virtual:zudoku-theme.css") {
|
|
270
|
-
return resolvedVirtualModuleId;
|
|
271
|
-
}
|
|
272
|
-
},
|
|
273
|
-
// Handle the virtual module for dynamic theme content
|
|
274
|
-
async load(id) {
|
|
275
|
-
if (id !== resolvedVirtualModuleId) return;
|
|
276
|
-
|
|
277
|
-
const config = getCurrentConfig();
|
|
278
|
-
|
|
279
|
-
const themeConfig = config.theme ?? {};
|
|
280
|
-
const themeCss: string[] = [];
|
|
281
|
-
|
|
282
|
-
// Consolidate all font processing
|
|
283
|
-
const fonts = await processFonts(themeConfig);
|
|
284
|
-
|
|
285
|
-
// Add all font imports at once
|
|
286
|
-
themeCss.push(
|
|
287
|
-
...fonts.bundled.map((specifier) => `@import '${specifier}';`),
|
|
288
|
-
...fonts.imports.map((url) => `@import url('${url}');`),
|
|
289
|
-
);
|
|
290
|
-
|
|
291
|
-
// Handle registry theme variables
|
|
292
|
-
if (themeConfig.registryUrl) {
|
|
293
|
-
try {
|
|
294
|
-
const registryItem = await fetchShadcnRegistryItem(
|
|
295
|
-
themeConfig.registryUrl,
|
|
296
|
-
);
|
|
297
|
-
|
|
298
|
-
// Extract theme variables from registry
|
|
299
|
-
const { cssVars = {}, css = {} } = registryItem;
|
|
300
|
-
const { light: regLightVars = {}, dark: regDarkVars = {} } = cssVars;
|
|
301
|
-
|
|
302
|
-
// Process custom CSS from registry
|
|
303
|
-
if (Object.keys(css).length > 0) {
|
|
304
|
-
themeCss.push(processCustomCss(css));
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
// Merge registry variables with user overrides
|
|
308
|
-
const applyOverrides = (
|
|
309
|
-
vars: Record<string, string>,
|
|
310
|
-
overrides?: Theme,
|
|
311
|
-
) => {
|
|
312
|
-
if (!overrides) return vars;
|
|
313
|
-
|
|
314
|
-
const result = { ...vars };
|
|
315
|
-
for (const [key, value] of objectEntries(overrides)) {
|
|
316
|
-
if (value && THEME_VARIABLES.includes(key)) {
|
|
317
|
-
result[uncamelize(key)] = processColorValue(value);
|
|
318
|
-
}
|
|
319
|
-
}
|
|
320
|
-
return result;
|
|
321
|
-
};
|
|
322
|
-
|
|
323
|
-
const lightVars = applyOverrides(regLightVars, themeConfig.light);
|
|
324
|
-
const darkVars = applyOverrides(regDarkVars, themeConfig.dark);
|
|
325
|
-
|
|
326
|
-
// Generate CSS for light and dark themes
|
|
327
|
-
if (Object.keys(lightVars).length > 0) {
|
|
328
|
-
themeCss.push(`:root {\n${generateRegistryCss(lightVars)}\n}`);
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
if (Object.keys(darkVars).length > 0) {
|
|
332
|
-
themeCss.push(`.dark {\n${generateRegistryCss(darkVars)}\n}`);
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
if (lightVars["letter-spacing"] !== "0em") {
|
|
336
|
-
themeCss.push(
|
|
337
|
-
"@theme inline {",
|
|
338
|
-
" --tracking-tighter: calc(var(--tracking-normal) - 0.05em);",
|
|
339
|
-
" --tracking-tight: calc(var(--tracking-normal) - 0.025em);",
|
|
340
|
-
" --tracking-normal: var(--tracking-normal);",
|
|
341
|
-
" --tracking-wide: calc(var(--tracking-normal) + 0.025em);",
|
|
342
|
-
" --tracking-wider: calc(var(--tracking-normal) + 0.05em);",
|
|
343
|
-
" --tracking-widest: calc(var(--tracking-normal) + 0.1em);",
|
|
344
|
-
"}",
|
|
345
|
-
);
|
|
346
|
-
themeCss.push(
|
|
347
|
-
`body {\n letter-spacing: var(--tracking-normal);\n}`,
|
|
348
|
-
);
|
|
349
|
-
}
|
|
350
|
-
} catch (error) {
|
|
351
|
-
// biome-ignore lint/suspicious/noConsole: Logging allowed here
|
|
352
|
-
console.error("Failed to load shadcn registry theme:", error);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
|
|
356
|
-
if (themeConfig.light) {
|
|
357
|
-
themeCss.push(`:root {\n${generateCss(themeConfig.light)}\n}`);
|
|
358
|
-
}
|
|
359
|
-
if (themeConfig.dark) {
|
|
360
|
-
themeCss.push(`.dark {\n${generateCss(themeConfig.dark)}\n}`);
|
|
361
|
-
}
|
|
362
|
-
|
|
363
|
-
// Add font family CSS variables
|
|
364
|
-
const rootVars = [
|
|
365
|
-
` --font-sans: ${fonts.families.sans};`,
|
|
366
|
-
` --font-mono: ${fonts.families.mono};`,
|
|
367
|
-
` --font-heading: ${fonts.families.heading};`,
|
|
368
|
-
];
|
|
369
|
-
|
|
370
|
-
if (fonts.families.serif) {
|
|
371
|
-
rootVars.push(` --font-serif: ${fonts.families.serif};`);
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// Make shiki theme background colors available as CSS variables
|
|
375
|
-
const shikiThemes =
|
|
376
|
-
config.syntaxHighlighting?.themes ?? defaultHighlightOptions.themes;
|
|
377
|
-
|
|
378
|
-
const [lightTheme, darkTheme] = (
|
|
379
|
-
await Promise.all<ThemeRegistration>([
|
|
380
|
-
import(`@shikijs/themes/${shikiThemes.light}`).then((m) => m.default),
|
|
381
|
-
import(`@shikijs/themes/${shikiThemes.dark}`).then((m) => m.default),
|
|
382
|
-
])
|
|
383
|
-
).map(normalizeTheme);
|
|
384
|
-
|
|
385
|
-
rootVars.push(
|
|
386
|
-
` --shiki-light: ${lightTheme?.fg ?? "#000"};`,
|
|
387
|
-
` --shiki-dark: ${darkTheme?.fg ?? "#fff"};`,
|
|
388
|
-
` --shiki-light-bg: ${lightTheme?.bg ?? "#fff"};`,
|
|
389
|
-
` --shiki-dark-bg: ${darkTheme?.bg ?? "#000"};`,
|
|
390
|
-
);
|
|
391
|
-
|
|
392
|
-
themeCss.push(`:root {\n${rootVars.join("\n")}\n}`);
|
|
393
|
-
|
|
394
|
-
return themeCss.join("\n");
|
|
395
|
-
},
|
|
396
|
-
|
|
397
|
-
// Handle static theme content in main.css
|
|
398
|
-
// This goes through the normal CSS pipeline so Tailwind can process it
|
|
399
|
-
async transform(src, id) {
|
|
400
|
-
if (!id.endsWith("/src/app/main.css")) return;
|
|
401
|
-
|
|
402
|
-
const config = getCurrentConfig();
|
|
403
|
-
|
|
404
|
-
const files = new Set(
|
|
405
|
-
[
|
|
406
|
-
config.__meta.rootDir,
|
|
407
|
-
...config.__meta.dependencies,
|
|
408
|
-
...(config.__pluginDirs ?? []),
|
|
409
|
-
...resolveModuleSourceDirs(config),
|
|
410
|
-
].map((file) => path.relative(path.dirname(id), file)),
|
|
411
|
-
);
|
|
412
|
-
|
|
413
|
-
const code = [...files].map((file) => `@source "${file}";`);
|
|
414
|
-
|
|
415
|
-
// NOTE: Font imports and declarations are handled by virtual:zudoku-theme.css
|
|
416
|
-
// This @theme block only maps CSS variables to Tailwind utilities
|
|
417
|
-
code.push("@theme inline {");
|
|
418
|
-
|
|
419
|
-
// prettier-ignore
|
|
420
|
-
const colorVars = [
|
|
421
|
-
"background", "foreground", "card", "card-foreground", "popover", "popover-foreground", "primary",
|
|
422
|
-
"primary-foreground", "secondary", "secondary-foreground", "muted", "muted-foreground", "accent",
|
|
423
|
-
"accent-foreground", "destructive", "destructive-foreground", "border", "input", "ring", "chart-1",
|
|
424
|
-
"chart-2", "chart-3", "chart-4", "chart-5", "sidebar", "sidebar-foreground", "sidebar-primary",
|
|
425
|
-
"sidebar-primary-foreground", "sidebar-accent", "sidebar-accent-foreground", "sidebar-border", "sidebar-ring",
|
|
426
|
-
];
|
|
427
|
-
|
|
428
|
-
code.push(
|
|
429
|
-
...colorVars.map((color) => ` --color-${color}: var(--${color});`),
|
|
430
|
-
);
|
|
431
|
-
|
|
432
|
-
code.push(
|
|
433
|
-
` --radius-sm: calc(var(--radius) - 4px);`,
|
|
434
|
-
` --radius-md: calc(var(--radius) - 2px);`,
|
|
435
|
-
` --radius-lg: var(--radius);`,
|
|
436
|
-
` --radius-xl: calc(var(--radius) + 4px);`,
|
|
437
|
-
` --font-sans: var(--font-sans);`,
|
|
438
|
-
` --font-mono: var(--font-mono);`,
|
|
439
|
-
` --font-serif: var(--font-serif);`,
|
|
440
|
-
);
|
|
441
|
-
|
|
442
|
-
code.push("}");
|
|
443
|
-
|
|
444
|
-
const customCss = config.theme?.customCss;
|
|
445
|
-
if (customCss) {
|
|
446
|
-
code.push(processCustomCss(customCss));
|
|
447
|
-
}
|
|
448
|
-
|
|
449
|
-
const defaultThemeImport = config.theme?.noDefaultTheme
|
|
450
|
-
? ""
|
|
451
|
-
: '@import "./defaultTheme.css" layer(theme);';
|
|
452
|
-
|
|
453
|
-
return src
|
|
454
|
-
.replace(DEFAULT_THEME_REPLACE, defaultThemeImport)
|
|
455
|
-
.replace(MAIN_REPLACE, code.join("\n"));
|
|
456
|
-
},
|
|
457
|
-
};
|
|
458
|
-
};
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import { normalizeTheme, type ThemeRegistration } from "shiki";
|
|
4
|
+
import type { Plugin } from "vite";
|
|
5
|
+
import type { ConfigWithMeta } from "../config/loader.js";
|
|
6
|
+
import { getCurrentConfig } from "../config/loader.js";
|
|
7
|
+
import type {
|
|
8
|
+
FontConfig,
|
|
9
|
+
ZudokuConfig,
|
|
10
|
+
} from "../config/validators/ZudokuConfig.js";
|
|
11
|
+
import { defaultHighlightOptions } from "../lib/shiki.js";
|
|
12
|
+
import { objectEntries } from "../lib/util/objectEntries.js";
|
|
13
|
+
import {
|
|
14
|
+
fetchShadcnRegistryItem,
|
|
15
|
+
type RegistryItemCss,
|
|
16
|
+
} from "./shadcn-registry.js";
|
|
17
|
+
|
|
18
|
+
// prettier-ignore
|
|
19
|
+
const THEME_VARIABLES = [
|
|
20
|
+
"background", "foreground", "card", "cardForeground", "popover", "popoverForeground", "primary",
|
|
21
|
+
"primaryForeground", "secondary", "secondaryForeground", "muted", "mutedForeground", "accent",
|
|
22
|
+
"accentForeground", "destructive", "destructiveForeground", "border", "input", "ring", "radius",
|
|
23
|
+
] as const;
|
|
24
|
+
|
|
25
|
+
const uncamelize = (str: string) =>
|
|
26
|
+
str.replace(/[A-Z]/g, (letter) => `-${letter.toLowerCase()}`);
|
|
27
|
+
|
|
28
|
+
export type Theme = Partial<Record<(typeof THEME_VARIABLES)[number], string>>;
|
|
29
|
+
|
|
30
|
+
const processColorValue = (value: string | undefined): string => {
|
|
31
|
+
if (!value) return "";
|
|
32
|
+
const color =
|
|
33
|
+
!value.startsWith("#") &&
|
|
34
|
+
!value.includes("(") &&
|
|
35
|
+
value.split(" ").length >= 3
|
|
36
|
+
? // Assume legacy tailwind hsl format
|
|
37
|
+
`hsl(${value})`
|
|
38
|
+
: value;
|
|
39
|
+
|
|
40
|
+
return color;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const generateCss = (theme: Theme) =>
|
|
44
|
+
objectEntries(theme)
|
|
45
|
+
.filter(([key]) => THEME_VARIABLES.includes(key))
|
|
46
|
+
.map(([key, value]) => `--${uncamelize(key)}: ${processColorValue(value)};`)
|
|
47
|
+
.join("\n");
|
|
48
|
+
|
|
49
|
+
const generateRegistryCss = (cssVars: Record<string, string>) =>
|
|
50
|
+
Object.entries(cssVars)
|
|
51
|
+
.map(([key, value]) => ` --${key}: ${value};`)
|
|
52
|
+
.join("\n");
|
|
53
|
+
|
|
54
|
+
const processCustomCss = (css: string | RegistryItemCss): string => {
|
|
55
|
+
if (typeof css === "string") {
|
|
56
|
+
return css;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const processStyles = (
|
|
60
|
+
styles: RegistryItemCss | string,
|
|
61
|
+
indent = "",
|
|
62
|
+
): string[] => {
|
|
63
|
+
if (typeof styles === "string") {
|
|
64
|
+
return [`${indent}${styles}`];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const rules = [];
|
|
68
|
+
for (const [key, value] of Object.entries(styles)) {
|
|
69
|
+
if (typeof value === "string") {
|
|
70
|
+
rules.push(`${indent}${key}: ${value};`);
|
|
71
|
+
} else {
|
|
72
|
+
rules.push(`${indent}${key} {`);
|
|
73
|
+
rules.push(...processStyles(value, `${indent} `));
|
|
74
|
+
rules.push(`${indent}}`);
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return rules;
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
return processStyles(css).join("\n");
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const MAIN_REPLACE = "/* @vite-plugin-inject main */";
|
|
84
|
+
const DEFAULT_THEME_REPLACE = "/* @vite-plugin-inject defaultTheme */";
|
|
85
|
+
|
|
86
|
+
// prettier-ignore
|
|
87
|
+
export const GOOGLE_FONTS = [
|
|
88
|
+
"Inter", "Roboto", "Open Sans", "Poppins", "Montserrat", "Outfit",
|
|
89
|
+
"Plus Jakarta Sans", "DM Sans", "IBM Plex Sans", "Geist", "Oxanium",
|
|
90
|
+
"Architects Daughter", "Merriweather", "Playfair Display", "Lora",
|
|
91
|
+
"Source Serif Pro", "Libre Baskerville", "Space Grotesk", "JetBrains Mono",
|
|
92
|
+
"Fira Code", "Source Code Pro", "IBM Plex Mono", "Roboto Mono", "Space Mono", "Geist Mono",
|
|
93
|
+
] as const;
|
|
94
|
+
type GoogleFont = (typeof GOOGLE_FONTS)[number];
|
|
95
|
+
|
|
96
|
+
const getGoogleFontUrl = (font: string) =>
|
|
97
|
+
`https://fonts.googleapis.com/css2?family=${font.replaceAll(/\s+/g, "+")}:wght@400;500;600;700&display=swap`;
|
|
98
|
+
|
|
99
|
+
const processFont = (fontValue: string) => {
|
|
100
|
+
const families = fontValue
|
|
101
|
+
.split(",")
|
|
102
|
+
.map((f) => f.trim().replaceAll(/['"]/g, ""));
|
|
103
|
+
|
|
104
|
+
return families
|
|
105
|
+
.filter((family) => GOOGLE_FONTS.includes(family as GoogleFont))
|
|
106
|
+
.map((family) => getGoogleFontUrl(family));
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
const processFontConfig = (fontConfig?: FontConfig) => {
|
|
110
|
+
if (!fontConfig) return { url: undefined, fontFamily: undefined };
|
|
111
|
+
|
|
112
|
+
if (typeof fontConfig === "string") {
|
|
113
|
+
if (GOOGLE_FONTS.includes(fontConfig as GoogleFont)) {
|
|
114
|
+
return {
|
|
115
|
+
url: getGoogleFontUrl(fontConfig),
|
|
116
|
+
fontFamily: fontConfig,
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
return { url: undefined, fontFamily: fontConfig };
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return {
|
|
123
|
+
url: fontConfig.url,
|
|
124
|
+
fontFamily: fontConfig.fontFamily,
|
|
125
|
+
};
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* The default fonts ship inside the portal (Latin subset, four weights) so a first paint makes no third-party
|
|
130
|
+
* request. Fonts a tenant picks by name still load from Google Fonts: that is their explicit choice.
|
|
131
|
+
*/
|
|
132
|
+
const bundledFont = (pkg: string) => {
|
|
133
|
+
// Absolute paths: the CSS is compiled from the site root, which does not see this package's dependencies.
|
|
134
|
+
const dir = path.dirname(
|
|
135
|
+
createRequire(import.meta.url).resolve(`@fontsource/${pkg}/package.json`),
|
|
136
|
+
);
|
|
137
|
+
return [400, 500, 600, 700].map((weight) =>
|
|
138
|
+
path.join(dir, `latin-${weight}.css`).split(path.sep).join("/"),
|
|
139
|
+
);
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const DEFAULT_FONT_SANS = '"IBM Plex Sans", system-ui, sans-serif';
|
|
143
|
+
const DEFAULT_FONT_MONO = '"JetBrains Mono", monospace';
|
|
144
|
+
const DEFAULT_FONT_HEADING = '"Space Grotesk", system-ui, sans-serif';
|
|
145
|
+
|
|
146
|
+
const processFonts = async (themeConfig: ZudokuConfig["theme"]) => {
|
|
147
|
+
const imports: string[] = [];
|
|
148
|
+
const bundled: string[] = [];
|
|
149
|
+
const families = {
|
|
150
|
+
sans: DEFAULT_FONT_SANS,
|
|
151
|
+
serif: undefined as string | undefined,
|
|
152
|
+
mono: DEFAULT_FONT_MONO,
|
|
153
|
+
heading: DEFAULT_FONT_HEADING,
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// Process user fonts
|
|
157
|
+
const userFonts = {
|
|
158
|
+
sans: processFontConfig(themeConfig?.fonts?.sans),
|
|
159
|
+
serif: processFontConfig(themeConfig?.fonts?.serif),
|
|
160
|
+
mono: processFontConfig(themeConfig?.fonts?.mono),
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
// Add user font imports
|
|
164
|
+
for (const font of Object.values(userFonts)) {
|
|
165
|
+
if (font.url) {
|
|
166
|
+
imports.push(font.url);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Process registry fonts if available
|
|
171
|
+
let registryFonts = {
|
|
172
|
+
sans: undefined as string | undefined,
|
|
173
|
+
serif: undefined as string | undefined,
|
|
174
|
+
mono: undefined as string | undefined,
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
if (themeConfig?.registryUrl) {
|
|
178
|
+
try {
|
|
179
|
+
const registryItem = await fetchShadcnRegistryItem(
|
|
180
|
+
themeConfig.registryUrl,
|
|
181
|
+
);
|
|
182
|
+
const { cssVars = {} } = registryItem;
|
|
183
|
+
const { theme: regThemeVars = {}, light: regLightVars = {} } = cssVars;
|
|
184
|
+
|
|
185
|
+
registryFonts = {
|
|
186
|
+
sans: regLightVars["font-sans"] ?? regThemeVars["font-sans"],
|
|
187
|
+
serif: regLightVars["font-serif"] ?? regThemeVars["font-serif"],
|
|
188
|
+
mono: regLightVars["font-mono"] ?? regThemeVars["font-mono"],
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
// Add registry font imports
|
|
192
|
+
for (const font of Object.values(registryFonts)) {
|
|
193
|
+
if (font) {
|
|
194
|
+
imports.push(...processFont(font));
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
} catch {
|
|
198
|
+
// Registry fonts failed to load, continue with user fonts and defaults
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Determine final font families with priority: user > registry > defaults
|
|
203
|
+
families.sans =
|
|
204
|
+
userFonts.sans.fontFamily || registryFonts.sans || DEFAULT_FONT_SANS;
|
|
205
|
+
families.serif = userFonts.serif.fontFamily || registryFonts.serif;
|
|
206
|
+
families.mono =
|
|
207
|
+
userFonts.mono.fontFamily || registryFonts.mono || DEFAULT_FONT_MONO;
|
|
208
|
+
|
|
209
|
+
// Add default font imports if no user or registry fonts
|
|
210
|
+
if (!userFonts.sans.fontFamily && !registryFonts.sans) {
|
|
211
|
+
bundled.push(...bundledFont("ibm-plex-sans"));
|
|
212
|
+
}
|
|
213
|
+
if (!userFonts.mono.fontFamily && !registryFonts.mono) {
|
|
214
|
+
bundled.push(...bundledFont("jetbrains-mono"));
|
|
215
|
+
}
|
|
216
|
+
bundled.push(...bundledFont("space-grotesk"));
|
|
217
|
+
|
|
218
|
+
return { imports, bundled, families };
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const MODULE_SOURCE_PACKAGES = [
|
|
222
|
+
"@lukoweb/apitogo-module-landing",
|
|
223
|
+
"@lukoweb/apitogo-module-user-panel",
|
|
224
|
+
];
|
|
225
|
+
|
|
226
|
+
/**
|
|
227
|
+
* Directories of module packages (landing, user panel) that use Tailwind
|
|
228
|
+
* utility classes. They live outside the Tailwind `source("..")` root set in
|
|
229
|
+
* main.css, so they must be added as explicit `@source` scan paths — otherwise
|
|
230
|
+
* their arbitrary-value utilities (e.g. the landing hero grid template and
|
|
231
|
+
* terminal card colors) are missing from the generated CSS.
|
|
232
|
+
*/
|
|
233
|
+
const resolveModuleSourceDirs = (config: ConfigWithMeta): string[] => {
|
|
234
|
+
if (config.__meta.mode === "internal") {
|
|
235
|
+
return MODULE_SOURCE_PACKAGES.map((pkg) =>
|
|
236
|
+
path.join(
|
|
237
|
+
config.__meta.moduleDir,
|
|
238
|
+
"..",
|
|
239
|
+
pkg.replace("@lukoweb/apitogo-", ""),
|
|
240
|
+
"src",
|
|
241
|
+
),
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
const require = createRequire(
|
|
246
|
+
path.join(config.__meta.rootDir, "package.json"),
|
|
247
|
+
);
|
|
248
|
+
const dirs: string[] = [];
|
|
249
|
+
for (const pkg of MODULE_SOURCE_PACKAGES) {
|
|
250
|
+
try {
|
|
251
|
+
dirs.push(path.dirname(require.resolve(pkg)));
|
|
252
|
+
} catch {
|
|
253
|
+
// Module package not installed — nothing to scan.
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
return dirs;
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
export const virtualModuleId = "virtual:zudoku-theme.css";
|
|
260
|
+
export const resolvedVirtualModuleId = `\0${virtualModuleId}`;
|
|
261
|
+
|
|
262
|
+
export const viteThemePlugin = (): Plugin => {
|
|
263
|
+
return {
|
|
264
|
+
// It is important to run before the Tailwind plugin
|
|
265
|
+
enforce: "pre",
|
|
266
|
+
name: "zudoku-css-theme",
|
|
267
|
+
|
|
268
|
+
resolveId(id) {
|
|
269
|
+
if (id === "virtual:zudoku-theme.css") {
|
|
270
|
+
return resolvedVirtualModuleId;
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
// Handle the virtual module for dynamic theme content
|
|
274
|
+
async load(id) {
|
|
275
|
+
if (id !== resolvedVirtualModuleId) return;
|
|
276
|
+
|
|
277
|
+
const config = getCurrentConfig();
|
|
278
|
+
|
|
279
|
+
const themeConfig = config.theme ?? {};
|
|
280
|
+
const themeCss: string[] = [];
|
|
281
|
+
|
|
282
|
+
// Consolidate all font processing
|
|
283
|
+
const fonts = await processFonts(themeConfig);
|
|
284
|
+
|
|
285
|
+
// Add all font imports at once
|
|
286
|
+
themeCss.push(
|
|
287
|
+
...fonts.bundled.map((specifier) => `@import '${specifier}';`),
|
|
288
|
+
...fonts.imports.map((url) => `@import url('${url}');`),
|
|
289
|
+
);
|
|
290
|
+
|
|
291
|
+
// Handle registry theme variables
|
|
292
|
+
if (themeConfig.registryUrl) {
|
|
293
|
+
try {
|
|
294
|
+
const registryItem = await fetchShadcnRegistryItem(
|
|
295
|
+
themeConfig.registryUrl,
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
// Extract theme variables from registry
|
|
299
|
+
const { cssVars = {}, css = {} } = registryItem;
|
|
300
|
+
const { light: regLightVars = {}, dark: regDarkVars = {} } = cssVars;
|
|
301
|
+
|
|
302
|
+
// Process custom CSS from registry
|
|
303
|
+
if (Object.keys(css).length > 0) {
|
|
304
|
+
themeCss.push(processCustomCss(css));
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
// Merge registry variables with user overrides
|
|
308
|
+
const applyOverrides = (
|
|
309
|
+
vars: Record<string, string>,
|
|
310
|
+
overrides?: Theme,
|
|
311
|
+
) => {
|
|
312
|
+
if (!overrides) return vars;
|
|
313
|
+
|
|
314
|
+
const result = { ...vars };
|
|
315
|
+
for (const [key, value] of objectEntries(overrides)) {
|
|
316
|
+
if (value && THEME_VARIABLES.includes(key)) {
|
|
317
|
+
result[uncamelize(key)] = processColorValue(value);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return result;
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
const lightVars = applyOverrides(regLightVars, themeConfig.light);
|
|
324
|
+
const darkVars = applyOverrides(regDarkVars, themeConfig.dark);
|
|
325
|
+
|
|
326
|
+
// Generate CSS for light and dark themes
|
|
327
|
+
if (Object.keys(lightVars).length > 0) {
|
|
328
|
+
themeCss.push(`:root {\n${generateRegistryCss(lightVars)}\n}`);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
if (Object.keys(darkVars).length > 0) {
|
|
332
|
+
themeCss.push(`.dark {\n${generateRegistryCss(darkVars)}\n}`);
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
if (lightVars["letter-spacing"] !== "0em") {
|
|
336
|
+
themeCss.push(
|
|
337
|
+
"@theme inline {",
|
|
338
|
+
" --tracking-tighter: calc(var(--tracking-normal) - 0.05em);",
|
|
339
|
+
" --tracking-tight: calc(var(--tracking-normal) - 0.025em);",
|
|
340
|
+
" --tracking-normal: var(--tracking-normal);",
|
|
341
|
+
" --tracking-wide: calc(var(--tracking-normal) + 0.025em);",
|
|
342
|
+
" --tracking-wider: calc(var(--tracking-normal) + 0.05em);",
|
|
343
|
+
" --tracking-widest: calc(var(--tracking-normal) + 0.1em);",
|
|
344
|
+
"}",
|
|
345
|
+
);
|
|
346
|
+
themeCss.push(
|
|
347
|
+
`body {\n letter-spacing: var(--tracking-normal);\n}`,
|
|
348
|
+
);
|
|
349
|
+
}
|
|
350
|
+
} catch (error) {
|
|
351
|
+
// biome-ignore lint/suspicious/noConsole: Logging allowed here
|
|
352
|
+
console.error("Failed to load shadcn registry theme:", error);
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (themeConfig.light) {
|
|
357
|
+
themeCss.push(`:root {\n${generateCss(themeConfig.light)}\n}`);
|
|
358
|
+
}
|
|
359
|
+
if (themeConfig.dark) {
|
|
360
|
+
themeCss.push(`.dark {\n${generateCss(themeConfig.dark)}\n}`);
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
// Add font family CSS variables
|
|
364
|
+
const rootVars = [
|
|
365
|
+
` --font-sans: ${fonts.families.sans};`,
|
|
366
|
+
` --font-mono: ${fonts.families.mono};`,
|
|
367
|
+
` --font-heading: ${fonts.families.heading};`,
|
|
368
|
+
];
|
|
369
|
+
|
|
370
|
+
if (fonts.families.serif) {
|
|
371
|
+
rootVars.push(` --font-serif: ${fonts.families.serif};`);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// Make shiki theme background colors available as CSS variables
|
|
375
|
+
const shikiThemes =
|
|
376
|
+
config.syntaxHighlighting?.themes ?? defaultHighlightOptions.themes;
|
|
377
|
+
|
|
378
|
+
const [lightTheme, darkTheme] = (
|
|
379
|
+
await Promise.all<ThemeRegistration>([
|
|
380
|
+
import(`@shikijs/themes/${shikiThemes.light}`).then((m) => m.default),
|
|
381
|
+
import(`@shikijs/themes/${shikiThemes.dark}`).then((m) => m.default),
|
|
382
|
+
])
|
|
383
|
+
).map(normalizeTheme);
|
|
384
|
+
|
|
385
|
+
rootVars.push(
|
|
386
|
+
` --shiki-light: ${lightTheme?.fg ?? "#000"};`,
|
|
387
|
+
` --shiki-dark: ${darkTheme?.fg ?? "#fff"};`,
|
|
388
|
+
` --shiki-light-bg: ${lightTheme?.bg ?? "#fff"};`,
|
|
389
|
+
` --shiki-dark-bg: ${darkTheme?.bg ?? "#000"};`,
|
|
390
|
+
);
|
|
391
|
+
|
|
392
|
+
themeCss.push(`:root {\n${rootVars.join("\n")}\n}`);
|
|
393
|
+
|
|
394
|
+
return themeCss.join("\n");
|
|
395
|
+
},
|
|
396
|
+
|
|
397
|
+
// Handle static theme content in main.css
|
|
398
|
+
// This goes through the normal CSS pipeline so Tailwind can process it
|
|
399
|
+
async transform(src, id) {
|
|
400
|
+
if (!id.endsWith("/src/app/main.css")) return;
|
|
401
|
+
|
|
402
|
+
const config = getCurrentConfig();
|
|
403
|
+
|
|
404
|
+
const files = new Set(
|
|
405
|
+
[
|
|
406
|
+
config.__meta.rootDir,
|
|
407
|
+
...config.__meta.dependencies,
|
|
408
|
+
...(config.__pluginDirs ?? []),
|
|
409
|
+
...resolveModuleSourceDirs(config),
|
|
410
|
+
].map((file) => path.relative(path.dirname(id), file)),
|
|
411
|
+
);
|
|
412
|
+
|
|
413
|
+
const code = [...files].map((file) => `@source "${file}";`);
|
|
414
|
+
|
|
415
|
+
// NOTE: Font imports and declarations are handled by virtual:zudoku-theme.css
|
|
416
|
+
// This @theme block only maps CSS variables to Tailwind utilities
|
|
417
|
+
code.push("@theme inline {");
|
|
418
|
+
|
|
419
|
+
// prettier-ignore
|
|
420
|
+
const colorVars = [
|
|
421
|
+
"background", "foreground", "card", "card-foreground", "popover", "popover-foreground", "primary",
|
|
422
|
+
"primary-foreground", "secondary", "secondary-foreground", "muted", "muted-foreground", "accent",
|
|
423
|
+
"accent-foreground", "destructive", "destructive-foreground", "border", "input", "ring", "chart-1",
|
|
424
|
+
"chart-2", "chart-3", "chart-4", "chart-5", "sidebar", "sidebar-foreground", "sidebar-primary",
|
|
425
|
+
"sidebar-primary-foreground", "sidebar-accent", "sidebar-accent-foreground", "sidebar-border", "sidebar-ring",
|
|
426
|
+
];
|
|
427
|
+
|
|
428
|
+
code.push(
|
|
429
|
+
...colorVars.map((color) => ` --color-${color}: var(--${color});`),
|
|
430
|
+
);
|
|
431
|
+
|
|
432
|
+
code.push(
|
|
433
|
+
` --radius-sm: calc(var(--radius) - 4px);`,
|
|
434
|
+
` --radius-md: calc(var(--radius) - 2px);`,
|
|
435
|
+
` --radius-lg: var(--radius);`,
|
|
436
|
+
` --radius-xl: calc(var(--radius) + 4px);`,
|
|
437
|
+
` --font-sans: var(--font-sans);`,
|
|
438
|
+
` --font-mono: var(--font-mono);`,
|
|
439
|
+
` --font-serif: var(--font-serif);`,
|
|
440
|
+
);
|
|
441
|
+
|
|
442
|
+
code.push("}");
|
|
443
|
+
|
|
444
|
+
const customCss = config.theme?.customCss;
|
|
445
|
+
if (customCss) {
|
|
446
|
+
code.push(processCustomCss(customCss));
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
const defaultThemeImport = config.theme?.noDefaultTheme
|
|
450
|
+
? ""
|
|
451
|
+
: '@import "./defaultTheme.css" layer(theme);';
|
|
452
|
+
|
|
453
|
+
return src
|
|
454
|
+
.replace(DEFAULT_THEME_REPLACE, defaultThemeImport)
|
|
455
|
+
.replace(MAIN_REPLACE, code.join("\n"));
|
|
456
|
+
},
|
|
457
|
+
};
|
|
458
|
+
};
|