Compare commits

..

19 Commits

Author SHA1 Message Date
HugoRCD
c326180f15 Merge remote-tracking branch 'origin/v3' into fix/3394 2025-05-20 14:58:39 +02:00
Benjamin Canac
6887e33aae chore(deps): update @nuxt/ui-pro 2025-05-20 14:22:31 +02:00
HugoRCD
d75093a160 revert 2025-05-19 16:05:36 +02:00
HugoRCD
47ed1e0f74 test 2025-05-19 15:56:18 +02:00
HugoRCD
d6a3a65b8e up 2025-05-19 14:14:34 +02:00
HugoRCD
a81d0e55c7 up 2025-05-19 13:35:23 +02:00
HugoRCD
5b172b0fb3 test 2025-05-19 12:48:33 +02:00
HugoRCD
fbf7475e0d up 2025-05-19 12:27:38 +02:00
HugoRCD
0f90645c84 up 2025-05-19 11:31:38 +02:00
HugoRCD
33193d782d up 2025-05-19 11:30:40 +02:00
HugoRCD
d1f2b50033 Merge remote-tracking branch 'origin/v3' into fix/3394 2025-05-19 11:26:00 +02:00
HugoRCD
bd75d2d184 up 2025-05-19 11:25:57 +02:00
renovate[bot]
28e869e8aa chore(deps): update all non-major dependencies (v3) (#4178)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-19 11:05:40 +02:00
HugoRCD
cabad480f9 test 2025-05-19 11:02:36 +02:00
zikju
d86956e1d5 feat(locale): add Lithuanian language (#4171)
Co-authored-by: Benjamin Canac <canacb1@gmail.com>
2025-05-19 10:53:02 +02:00
renovate[bot]
23e4f0ec4d chore(deps): update tailwindcss to ^4.1.7 (v3) (#4179)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2025-05-19 10:52:00 +02:00
HugoRCD
91d06d4d51 up 2025-05-19 10:49:49 +02:00
HugoRCD
f1128c2450 feat: implement csp, sty-src with nonce 2025-05-19 10:37:46 +02:00
Muhammad Syahmi Mohd Ikram
c00f6e8cdf feat(locale): add Malay language (#4160) 2025-05-16 12:14:21 +02:00
19 changed files with 691 additions and 965 deletions

View File

@@ -12,6 +12,7 @@ const { data: files } = useLazyAsyncData('search', () => queryCollectionSearchSe
}) })
const links = useLinks() const links = useLinks()
const searchLinks = useSearchLinks()
const color = computed(() => colorMode.value === 'dark' ? (colors as any)[appConfig.ui.colors.neutral][900] : 'white') const color = computed(() => colorMode.value === 'dark' ? (colors as any)[appConfig.ui.colors.neutral][900] : 'white')
const radius = computed(() => `:root { --ui-radius: ${appConfig.theme.radius}rem; }`) const radius = computed(() => `:root { --ui-radius: ${appConfig.theme.radius}rem; }`)
const blackAsPrimary = computed(() => appConfig.theme.blackAsPrimary ? `:root { --ui-primary: black; } .dark { --ui-primary: white; }` : ':root {}') const blackAsPrimary = computed(() => appConfig.theme.blackAsPrimary ? `:root { --ui-primary: black; } .dark { --ui-primary: white; }` : ':root {}')
@@ -41,6 +42,7 @@ useServerSeoMeta({
useFaviconFromTheme() useFaviconFromTheme()
const { frameworks, modules } = useSharedData()
const { mappedNavigation, filteredNavigation } = useContentNavigation(navigation) const { mappedNavigation, filteredNavigation } = useContentNavigation(navigation)
provide('navigation', mappedNavigation) provide('navigation', mappedNavigation)
@@ -63,7 +65,23 @@ provide('navigation', mappedNavigation)
<template v-if="!route.path.startsWith('/examples')"> <template v-if="!route.path.startsWith('/examples')">
<Footer /> <Footer />
<Search :files="files" :navigation="filteredNavigation" /> <ClientOnly>
<LazyUContentSearch
:links="searchLinks"
:files="files"
:groups="[{
id: 'framework',
label: 'Framework',
items: frameworks
}, {
id: 'module',
label: 'Module',
items: modules
}]"
:navigation="filteredNavigation"
:fuse="{ resultLimit: 100 }"
/>
</ClientOnly>
</template> </template>
</UApp> </UApp>
</template> </template>

View File

@@ -1,199 +0,0 @@
<script setup lang="ts">
import type { DefineComponent } from 'vue'
import type { ContentNavigationItem } from '@nuxt/content'
import { useChat } from '@ai-sdk/vue'
import ProseStreamPre from './prose/PreStream.vue'
const components = {
pre: ProseStreamPre as unknown as DefineComponent
}
interface ContentSearchFile {
id: string
title: string
titles: string[]
level: number
content: string
}
defineProps<{
files?: ContentSearchFile[]
navigation?: ContentNavigationItem[]
}>()
const { frameworks, modules } = useSharedData()
const { messages, input, handleSubmit, status, error, reload, setMessages } = useChat({
maxSteps: 2
})
const ai = ref(false)
const searchTerm = ref('')
const links = computed(() => [{
label: 'Ask AI',
icon: 'i-lucide-bot',
onSelect: (e: any) => {
e.preventDefault()
ai.value = true
}
}, {
label: 'Docs',
icon: 'i-lucide-square-play',
to: '/getting-started'
}, {
label: 'Components',
icon: 'i-lucide-square-code',
to: '/components'
}, {
icon: 'i-lucide-sparkles',
label: 'Pro > Features',
description: 'A collection of premium Vue components.',
to: '/pro'
}, {
icon: 'i-lucide-credit-card',
label: 'Pro > Pricing',
description: 'Free in development, buy when ready to launch.',
to: '/pro/pricing'
}, {
icon: 'i-lucide-panels-top-left',
label: 'Pro > Templates',
description: 'Official templates made with Nuxt UI Pro.',
to: '/pro/templates'
}, {
icon: 'i-lucide-circle-check',
label: 'Pro > Activate',
description: 'Enable Nuxt UI Pro in your production projects.',
to: '/pro/activate'
}, {
label: 'Figma',
icon: 'i-simple-icons-figma',
to: '/figma'
}, {
icon: 'i-lucide-presentation',
label: 'Community > Showcase',
description: 'Check out some of the amazing projects built with Nuxt UI.',
to: '/showcase'
}, {
label: 'Community > Contribution',
description: 'A comprehensive guide on contributing to Nuxt UI, including project structure, development workflow, and best practices.',
icon: 'i-lucide-git-pull-request-arrow',
to: '/getting-started/contribution'
}, {
label: 'Community > Roadmap',
description: 'Track our development progress in real-time.',
icon: 'i-lucide-map',
to: '/roadmap'
}, {
label: 'Community > Devtools',
description: 'Integrate Nuxt UI with Nuxt Devtools with Compodium.',
icon: 'i-lucide-code',
to: 'https://github.com/romhml/compodium',
target: '_blank'
}, {
label: 'Community > Team',
description: 'Meet the team behind Nuxt UI.',
icon: 'i-lucide-users',
to: '/team'
}, {
label: 'Releases',
icon: 'i-lucide-rocket',
to: 'https://github.com/nuxt/ui/releases',
target: '_blank'
}])
const groups = computed(() => [{
id: 'ai',
label: 'AI',
ignoreFilter: true,
items: [{
label: searchTerm.value ? `Ask Nuxt AI for “${searchTerm.value}` : 'Ask Nuxt AI',
icon: 'i-lucide-bot',
onSelect: (e: any) => {
e.preventDefault()
ai.value = true
if (searchTerm.value) {
setMessages([{
id: '1',
role: 'user',
content: searchTerm.value
}])
reload()
}
}
}]
}, {
id: 'framework',
label: 'Framework',
items: frameworks.value
}, {
id: 'module',
label: 'Module',
items: modules.value
}])
function onClose(e: Event) {
console.log('onClose')
e.preventDefault()
ai.value = false
}
</script>
<template>
<LazyUContentSearch
v-model:search-term="searchTerm"
:links="links"
:files="files"
:groups="groups"
:navigation="navigation"
:fuse="{ resultLimit: 100 }"
>
<template v-if="ai" #content>
<UChatPalette>
<UChatMessages
:messages="messages"
:status="status"
:user="{ side: 'left', variant: 'naked', icon: 'i-lucide-user' }"
:assistant="{ icon: 'i-lucide-bot' }"
>
<template #content="{ message }">
<MDCCached
v-if="message.toolInvocations?.[0]?.state === 'result'"
:value="message.toolInvocations?.[0]?.result"
:cache-key="message.id"
unwrap="p"
:components="components"
:parser-options="{ highlight: false }"
/>
<MDCCached
v-else-if="message.content.length > 0"
:value="message.content"
:cache-key="message.id"
unwrap="p"
:components="components"
:parser-options="{ highlight: false }"
/>
<span v-else class="italic font-light">
Searching documentation...
</span>
</template>
</UChatMessages>
<template #prompt>
<UChatPrompt
v-model="input"
icon="i-lucide-search"
variant="naked"
:error="error"
@submit="handleSubmit"
@close="onClose"
/>
</template>
</UChatPalette>
</template>
</LazyUContentSearch>
</template>

View File

@@ -25,6 +25,7 @@ function getEmojiFlag(locale: string): string {
kk: 'kz', // Kazakh -> Kazakhstan kk: 'kz', // Kazakh -> Kazakhstan
km: 'kh', // Khmer -> Cambodia km: 'kh', // Khmer -> Cambodia
ko: 'kr', // Korean -> South Korea ko: 'kr', // Korean -> South Korea
ms: 'my', // Malay -> Malaysia
nb: 'no', // Norwegian Bokmål -> Norway nb: 'no', // Norwegian Bokmål -> Norway
sl: 'si', // Slovenian -> Slovenia sl: 'si', // Slovenian -> Slovenia
sv: 'se', // Swedish -> Sweden sv: 'se', // Swedish -> Sweden

View File

@@ -1,44 +0,0 @@
<script setup lang="ts">
import { ShikiCachedRenderer } from 'shiki-stream/vue'
const colorMode = useColorMode()
const highlighter = await useHighlighter()
const props = defineProps<{
code: string
language: string
class?: string
meta?: string
}>()
const trimmedCode = computed(() => {
return props.code.trim().replace(/`+$/, '')
})
const lang = computed(() => {
switch (props.language) {
case 'vue':
return 'vue'
case 'javascript':
return 'js'
case 'typescript':
return 'ts'
case 'css':
return 'css'
default:
return props.language
}
})
const key = computed(() => {
return `${lang.value}-${colorMode.value}`
})
</script>
<template>
<ProsePre v-bind="props">
<ShikiCachedRenderer
:key="key"
:highlighter="highlighter"
:code="trimmedCode"
:lang="lang"
:theme="colorMode.value === 'dark' ? 'material-theme-palenight' : 'material-theme-lighter'"
/>
</ProsePre>
</template>

View File

@@ -1,21 +0,0 @@
import { createHighlighter, type HighlighterGeneric } from 'shiki'
import { createJavaScriptRegexEngine } from 'shiki/engine-javascript.mjs'
let highlighter: HighlighterGeneric<any, any> | null = null
let promise: Promise<HighlighterGeneric<any, any>> | null = null
export const useHighlighter = async () => {
if (!promise) {
promise = createHighlighter({
langs: ['vue', 'js', 'ts', 'css', 'html', 'json', 'yaml', 'markdown', 'bash'],
themes: ['material-theme-palenight', 'material-theme-lighter'],
engine: createJavaScriptRegexEngine()
})
}
if (!highlighter) {
highlighter = await promise
}
return highlighter
}

View File

@@ -0,0 +1,66 @@
export function useSearchLinks() {
return [{
label: 'Docs',
icon: 'i-lucide-square-play',
to: '/getting-started'
}, {
label: 'Components',
icon: 'i-lucide-square-code',
to: '/components'
}, {
icon: 'i-lucide-sparkles',
label: 'Pro > Features',
description: 'A collection of premium Vue components.',
to: '/pro'
}, {
icon: 'i-lucide-credit-card',
label: 'Pro > Pricing',
description: 'Free in development, buy when ready to launch.',
to: '/pro/pricing'
}, {
icon: 'i-lucide-panels-top-left',
label: 'Pro > Templates',
description: 'Official templates made with Nuxt UI Pro.',
to: '/pro/templates'
}, {
icon: 'i-lucide-circle-check',
label: 'Pro > Activate',
description: 'Enable Nuxt UI Pro in your production projects.',
to: '/pro/activate'
}, {
label: 'Figma',
icon: 'i-simple-icons-figma',
to: '/figma'
}, {
icon: 'i-lucide-presentation',
label: 'Community > Showcase',
description: 'Check out some of the amazing projects built with Nuxt UI.',
to: '/showcase'
}, {
label: 'Community > Contribution',
description: 'A comprehensive guide on contributing to Nuxt UI, including project structure, development workflow, and best practices.',
icon: 'i-lucide-git-pull-request-arrow',
to: '/getting-started/contribution'
}, {
label: 'Community > Roadmap',
description: 'Track our development progress in real-time.',
icon: 'i-lucide-map',
to: '/roadmap'
}, {
label: 'Community > Devtools',
description: 'Integrate Nuxt UI with Nuxt Devtools with Compodium.',
icon: 'i-lucide-code',
to: 'https://github.com/romhml/compodium',
target: '_blank'
}, {
label: 'Community > Team',
description: 'Meet the team behind Nuxt UI.',
icon: 'i-lucide-users',
to: '/team'
}, {
label: 'Releases',
icon: 'i-lucide-rocket',
to: 'https://github.com/nuxt/ui/releases',
target: '_blank'
}]
}

View File

@@ -15,6 +15,7 @@ const { data: files } = useLazyAsyncData('search', () => queryCollectionSearchSe
}) })
const links = useLinks() const links = useLinks()
const searchLinks = useSearchLinks()
const color = computed(() => colorMode.value === 'dark' ? (colors as any)[appConfig.ui.colors.neutral][900] : 'white') const color = computed(() => colorMode.value === 'dark' ? (colors as any)[appConfig.ui.colors.neutral][900] : 'white')
const radius = computed(() => `:root { --ui-radius: ${appConfig.theme.radius}rem; }`) const radius = computed(() => `:root { --ui-radius: ${appConfig.theme.radius}rem; }`)
const blackAsPrimary = computed(() => appConfig.theme.blackAsPrimary ? `:root { --ui-primary: black; } .dark { --ui-primary: white; }` : ':root {}') const blackAsPrimary = computed(() => appConfig.theme.blackAsPrimary ? `:root { --ui-primary: black; } .dark { --ui-primary: white; }` : ':root {}')
@@ -48,6 +49,7 @@ useServerSeoMeta({
useFaviconFromTheme() useFaviconFromTheme()
const { frameworks, modules } = useSharedData()
const { mappedNavigation, filteredNavigation } = useContentNavigation(navigation) const { mappedNavigation, filteredNavigation } = useContentNavigation(navigation)
provide('navigation', mappedNavigation) provide('navigation', mappedNavigation)
@@ -65,6 +67,22 @@ provide('navigation', mappedNavigation)
<Footer /> <Footer />
<Search :files="files" :navigation="filteredNavigation" /> <ClientOnly>
<LazyUContentSearch
:links="searchLinks"
:files="files"
:groups="[{
id: 'framework',
label: 'Framework',
items: frameworks
}, {
id: 'module',
label: 'Module',
items: modules
}]"
:navigation="filteredNavigation"
:fuse="{ resultLimit: 100 }"
/>
</ClientOnly>
</UApp> </UApp>
</template> </template>

View File

@@ -5,20 +5,20 @@
"dependencies": { "dependencies": {
"@ai-sdk/vue": "^1.2.12", "@ai-sdk/vue": "^1.2.12",
"@iconify-json/logos": "^1.2.4", "@iconify-json/logos": "^1.2.4",
"@iconify-json/lucide": "^1.2.43", "@iconify-json/lucide": "^1.2.44",
"@iconify-json/simple-icons": "^1.2.34", "@iconify-json/simple-icons": "^1.2.34",
"@iconify-json/vscode-icons": "^1.2.21", "@iconify-json/vscode-icons": "^1.2.21",
"@nuxt/content": "^3.5.1", "@nuxt/content": "^3.5.1",
"@nuxt/image": "^1.10.0", "@nuxt/image": "^1.10.0",
"@nuxt/ui": "latest", "@nuxt/ui": "latest",
"@nuxt/ui-pro": "^3.1.2", "@nuxt/ui-pro": "https://pkg.pr.new/@nuxt/ui-pro@9038c43",
"@nuxthub/core": "^0.8.27", "@nuxthub/core": "^0.8.27",
"@nuxtjs/plausible": "^1.2.0", "@nuxtjs/plausible": "^1.2.0",
"@octokit/rest": "^21.1.1", "@octokit/rest": "^21.1.1",
"@rollup/plugin-yaml": "^4.1.2", "@rollup/plugin-yaml": "^4.1.2",
"@vueuse/integrations": "^13.2.0", "@vueuse/integrations": "^13.2.0",
"@vueuse/nuxt": "^13.2.0", "@vueuse/nuxt": "^13.2.0",
"ai": "^4.3.15", "ai": "^4.3.16",
"capture-website": "^4.2.0", "capture-website": "^4.2.0",
"joi": "^17.13.3", "joi": "^17.13.3",
"motion-v": "^1.0.2", "motion-v": "^1.0.2",
@@ -27,7 +27,6 @@
"nuxt-llms": "^0.1.2", "nuxt-llms": "^0.1.2",
"nuxt-og-image": "^5.1.3", "nuxt-og-image": "^5.1.3",
"prettier": "^3.5.3", "prettier": "^3.5.3",
"shiki-stream": "^0.1.2",
"shiki-transformer-color-highlight": "^1.0.0", "shiki-transformer-color-highlight": "^1.0.0",
"sortablejs": "^1.15.6", "sortablejs": "^1.15.6",
"superstruct": "^2.0.2", "superstruct": "^2.0.2",

View File

@@ -1,6 +1,5 @@
import { streamText, tool } from 'ai' import { streamText } from 'ai'
import { createWorkersAI } from 'workers-ai-provider' import { createWorkersAI } from 'workers-ai-provider'
import { z } from 'zod'
export default defineEventHandler(async (event) => { export default defineEventHandler(async (event) => {
const { messages } = await readBody(event) const { messages } = await readBody(event)
@@ -13,30 +12,11 @@ export default defineEventHandler(async (event) => {
} }
: undefined : undefined
const workersAI = createWorkersAI({ binding: hubAI(), gateway }) const workersAI = createWorkersAI({ binding: hubAI(), gateway })
const autorag = hubAutoRAG('ui3')
return streamText({ return streamText({
model: workersAI('@cf/meta/llama-3.3-70b-instruct-fp8-fast'), model: workersAI('@cf/meta/llama-3.2-3b-instruct'),
messages, maxTokens: 10000,
system: `You are a helpful assistant for Nuxt UI. Check your knowledge base before answering any questions. system: 'You are a helpful assistant that can answer questions and help.',
Only respond to questions using information from tool calls. messages
if no relevant information is found in the tool calls, respond, "Sorry, I don't know."
Format your markdown response using the following rules:
- Use the vue lang for code blocks syntax highlighting.
- Don't use markdown headings.
`,
tools: {
searchDocumentation: tool({
description: `search the documentation for information to answer questions.`,
parameters: z.object({
question: z.string().describe('the users question')
}),
execute: async ({ question }) => {
return (await autorag.aiSearch({
query: question
})).response
}
})
}
}).toDataStreamResponse() }).toDataStreamResponse()
}) })

View File

@@ -116,15 +116,15 @@
"@internationalized/date": "^3.8.0", "@internationalized/date": "^3.8.0",
"@internationalized/number": "^3.6.1", "@internationalized/number": "^3.6.1",
"@nuxt/fonts": "^0.11.4", "@nuxt/fonts": "^0.11.4",
"@nuxt/icon": "^1.12.0", "@nuxt/icon": "^1.13.0",
"@nuxt/kit": "^3.17.3", "@nuxt/kit": "^3.17.3",
"@nuxt/schema": "^3.17.3", "@nuxt/schema": "^3.17.3",
"@nuxtjs/color-mode": "^3.5.2", "@nuxtjs/color-mode": "^3.5.2",
"@standard-schema/spec": "^1.0.0", "@standard-schema/spec": "^1.0.0",
"@tailwindcss/postcss": "^4.1.6", "@tailwindcss/postcss": "^4.1.7",
"@tailwindcss/vite": "^4.1.6", "@tailwindcss/vite": "^4.1.7",
"@tanstack/vue-table": "^8.21.3", "@tanstack/vue-table": "^8.21.3",
"@unhead/vue": "^2.0.8", "@unhead/vue": "^2.0.9",
"@vueuse/core": "^13.2.0", "@vueuse/core": "^13.2.0",
"@vueuse/integrations": "^13.2.0", "@vueuse/integrations": "^13.2.0",
"colortranslator": "^4.1.0", "colortranslator": "^4.1.0",
@@ -147,7 +147,7 @@
"reka-ui": "^2.2.1", "reka-ui": "^2.2.1",
"scule": "^1.3.0", "scule": "^1.3.0",
"tailwind-variants": "^1.0.0", "tailwind-variants": "^1.0.0",
"tailwindcss": "^4.1.6", "tailwindcss": "^4.1.7",
"tinyglobby": "^0.2.13", "tinyglobby": "^0.2.13",
"unplugin": "^2.3.4", "unplugin": "^2.3.4",
"unplugin-auto-import": "^19.2.0", "unplugin-auto-import": "^19.2.0",
@@ -156,13 +156,13 @@
"vue-component-type-helpers": "^2.2.10" "vue-component-type-helpers": "^2.2.10"
}, },
"devDependencies": { "devDependencies": {
"@nuxt/eslint-config": "^1.3.1", "@nuxt/eslint-config": "^1.4.0",
"@nuxt/module-builder": "^1.0.1", "@nuxt/module-builder": "^1.0.1",
"@nuxt/test-utils": "^3.18.0", "@nuxt/test-utils": "^3.19.0",
"@release-it/conventional-changelog": "^10.0.1", "@release-it/conventional-changelog": "^10.0.1",
"@vue/test-utils": "^2.4.6", "@vue/test-utils": "^2.4.6",
"embla-carousel": "^8.6.0", "embla-carousel": "^8.6.0",
"eslint": "^9.26.0", "eslint": "^9.27.0",
"happy-dom": "^17.4.7", "happy-dom": "^17.4.7",
"nuxt": "^3.17.3", "nuxt": "^3.17.3",
"release-it": "^19.0.2", "release-it": "^19.0.2",

View File

@@ -9,7 +9,7 @@
"typecheck": "nuxt typecheck" "typecheck": "nuxt typecheck"
}, },
"dependencies": { "dependencies": {
"@iconify-json/lucide": "^1.2.43", "@iconify-json/lucide": "^1.2.44",
"@iconify-json/simple-icons": "^1.2.34", "@iconify-json/simple-icons": "^1.2.34",
"@nuxt/ui": "latest", "@nuxt/ui": "latest",
"@nuxthub/core": "^0.8.27", "@nuxthub/core": "^0.8.27",

1072
pnpm-lock.yaml generated

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,7 @@ import icons from './theme/icons'
import { pick } from './runtime/utils' import { pick } from './runtime/utils'
export const getDefaultUiConfig = (colors?: string[]) => ({ export const getDefaultUiConfig = (colors?: string[], csp?: { nonce?: string }) => ({
colors: pick({ colors: pick({
primary: 'green', primary: 'green',
secondary: 'blue', secondary: 'blue',
@@ -12,7 +12,10 @@ export const getDefaultUiConfig = (colors?: string[]) => ({
error: 'red', error: 'red',
neutral: 'slate' neutral: 'slate'
}, [...(colors || []), 'neutral' as any]), }, [...(colors || []), 'neutral' as any]),
icons icons,
csp: csp || {
nonce: ''
}
}) })
export const defaultOptions = { export const defaultOptions = {
@@ -22,6 +25,9 @@ export const defaultOptions = {
theme: { theme: {
colors: undefined, colors: undefined,
transitions: true transitions: true
},
csp: {
nonce: ''
} }
} }

View File

@@ -28,6 +28,19 @@ export interface ModuleOptions {
*/ */
colorMode?: boolean colorMode?: boolean
/**
* Configure Content Security Policy for Nuxt UI
* @defaultValue `{ nonce: '' }`
* @link https://ui.nuxt.com/getting-started/installation/nuxt#csp
*/
csp?: {
/**
* Enable nonce for inline styles.
* @defaultValue ``
*/
nonce?: string
}
/** /**
* Customize how the theme is generated * Customize how the theme is generated
* @link https://ui.nuxt.com/getting-started/theme * @link https://ui.nuxt.com/getting-started/theme
@@ -70,7 +83,7 @@ export default defineNuxtModule<ModuleOptions>({
nuxt.options.alias['#ui'] = resolve('./runtime') nuxt.options.alias['#ui'] = resolve('./runtime')
nuxt.options.appConfig.ui = defu(nuxt.options.appConfig.ui || {}, getDefaultUiConfig(options.theme.colors)) nuxt.options.appConfig.ui = defu(nuxt.options.appConfig.ui || {}, getDefaultUiConfig(options.theme.colors, options.csp))
// Isolate root node from portaled components // Isolate root node from portaled components
nuxt.options.app.rootAttrs = nuxt.options.app.rootAttrs || {} nuxt.options.app.rootAttrs = nuxt.options.app.rootAttrs || {}

View File

@@ -21,9 +21,11 @@ export { default as hy } from './hy'
export { default as id } from './id' export { default as id } from './id'
export { default as it } from './it' export { default as it } from './it'
export { default as ja } from './ja' export { default as ja } from './ja'
export { default as km } from './km'
export { default as kk } from './kk' export { default as kk } from './kk'
export { default as km } from './km'
export { default as ko } from './ko' export { default as ko } from './ko'
export { default as lt } from './lt'
export { default as ms } from './ms'
export { default as nb_no } from './nb_no' export { default as nb_no } from './nb_no'
export { default as nl } from './nl' export { default as nl } from './nl'
export { default as pl } from './pl' export { default as pl } from './pl'
@@ -37,8 +39,8 @@ export { default as sv } from './sv'
export { default as th } from './th' export { default as th } from './th'
export { default as tj } from './tj' export { default as tj } from './tj'
export { default as tr } from './tr' export { default as tr } from './tr'
export { default as uk } from './uk'
export { default as ug_cn } from './ug_cn' export { default as ug_cn } from './ug_cn'
export { default as uk } from './uk'
export { default as ur } from './ur' export { default as ur } from './ur'
export { default as uz } from './uz' export { default as uz } from './uz'
export { default as vi } from './vi' export { default as vi } from './vi'

56
src/runtime/locale/lt.ts Normal file
View File

@@ -0,0 +1,56 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale'
export default defineLocale<Messages>({
name: 'Lietuvių',
code: 'lt',
messages: {
inputMenu: {
noMatch: 'Nėra atitinkančių duomenų',
noData: 'Nėra duomenų',
create: 'Sukurti „{label}“'
},
calendar: {
prevYear: 'Ankstesni metai',
nextYear: 'Kiti metai',
prevMonth: 'Ankstesnis mėnuo',
nextMonth: 'Kitas mėnuo'
},
inputNumber: {
increment: 'Padidinti',
decrement: 'Sumažinti'
},
commandPalette: {
placeholder: 'Įveskite komandą arba ieškokite...',
noMatch: 'Nėra atitinkančių duomenų',
noData: 'Nėra duomenų',
close: 'Uždaryti'
},
selectMenu: {
noMatch: 'Nėra atitinkančių duomenų',
noData: 'Nėra duomenų',
create: 'Sukurti „{label}“',
search: 'Ieškoti...'
},
toast: {
close: 'Uždaryti'
},
carousel: {
prev: 'Atgal',
next: 'Pirmyn',
goto: 'Eiti į skaidrę {slide}'
},
modal: {
close: 'Uždaryti'
},
slideover: {
close: 'Uždaryti'
},
alert: {
close: 'Uždaryti'
},
table: {
noData: 'Nėra duomenų'
}
}
})

56
src/runtime/locale/ms.ts Normal file
View File

@@ -0,0 +1,56 @@
import type { Messages } from '../types'
import { defineLocale } from '../composables/defineLocale'
export default defineLocale<Messages>({
name: 'Melayu',
code: 'ms',
messages: {
inputMenu: {
noMatch: 'Tiada data yang sepadan',
noData: 'Tiada data',
create: 'Cipta "{label}"'
},
calendar: {
prevYear: 'Tahun sebelum',
nextYear: 'Tahun seterusnya',
prevMonth: 'Bulan sebelum',
nextMonth: 'Bulan seterusnya'
},
inputNumber: {
increment: 'Naikkan',
decrement: 'Kurangkan'
},
commandPalette: {
placeholder: 'Taip arahan atau carian...',
noMatch: 'Tiada data yang sepadan',
noData: 'Tiada data',
close: 'Tutup'
},
selectMenu: {
noMatch: 'Tiada data yang sepadan',
noData: 'Tiada data',
create: 'Cipta "{label}"',
search: 'Cari...'
},
toast: {
close: 'Tutup'
},
carousel: {
prev: 'Sebelum',
next: 'Seterusnya',
goto: 'Pergi ke slaid {slide}'
},
modal: {
close: 'Tutup'
},
slideover: {
close: 'Tutup'
},
alert: {
close: 'Tutup'
},
table: {
noData: 'Tiada data'
}
}
})

View File

@@ -23,6 +23,8 @@ export default defineNuxtPlugin(() => {
const appConfig = useAppConfig() const appConfig = useAppConfig()
const nuxtApp = useNuxtApp() const nuxtApp = useNuxtApp()
const nonce = computed(() => appConfig.ui?.csp?.nonce)
const root = computed(() => { const root = computed(() => {
const { neutral, ...colors } = appConfig.ui.colors const { neutral, ...colors } = appConfig.ui.colors
@@ -44,7 +46,8 @@ export default defineNuxtPlugin(() => {
style: [{ style: [{
innerHTML: () => root.value, innerHTML: () => root.value,
tagPriority: -2, tagPriority: -2,
id: 'nuxt-ui-colors' id: 'nuxt-ui-colors',
...(nonce.value ? { nonce: nonce.value } : {})
}] }]
} }
@@ -54,10 +57,15 @@ export default defineNuxtPlugin(() => {
style.innerHTML = root.value style.innerHTML = root.value
style.setAttribute('data-nuxt-ui-colors', '') style.setAttribute('data-nuxt-ui-colors', '')
if (nonce.value) {
style.setAttribute('nonce', nonce.value)
}
document.head.appendChild(style) document.head.appendChild(style)
headData.script = [{ headData.script = [{
innerHTML: 'document.head.removeChild(document.querySelector(\'[data-nuxt-ui-colors]\'))' innerHTML: 'document.head.removeChild(document.querySelector(\'[data-nuxt-ui-colors]\'))',
...(nonce.value ? { nonce: nonce.value } : {})
}] }]
} }

View File

@@ -165,6 +165,9 @@ type AppConfigUI = {
} }
icons?: Partial<typeof icons> icons?: Partial<typeof icons>
tv?: typeof defaultConfig tv?: typeof defaultConfig
csp?: {
nonce?: string
}
} & TVConfig<typeof ui> } & TVConfig<typeof ui>
declare module '@nuxt/schema' { declare module '@nuxt/schema' {