feat: add Cloudinary media provider and Groq & Gemini AI providers - #537
feat: add Cloudinary media provider and Groq & Gemini AI providers#537narr07 wants to merge 591 commits into
Conversation
Co-authored-by: Baptiste Leproux <leproux.baptiste@gmail.com>
Co-authored-by: Baptiste Leproux <leproux.baptiste@gmail.com>
Co-authored-by: Baptiste Leproux <leproux.baptiste@gmail.com>
…udio editor (nuxt-content#254) Co-authored-by: Baptiste Leproux <leproux.baptiste@gmail.com>
Co-authored-by: Nuxt Studio <noreply@nuxt.studio>
Co-authored-by: Nuxt Studio <noreply@nuxt.studio>
…tent#507) Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
… (nuxt-content#510) Since nuxt-content#478 moved env reads into the studio-env runtime middleware, each provider's `moderators` defaults to `''` instead of `undefined`. The OAuth handlers parsed it as `''.split(',')` -> `['']` (length 1), so the `moderators.length > 0` guard fired and rejected every user with a 403, even when no allowlist was configured. Filter out blank entries so an unset/blank allowlist yields `[]`. Signed-off-by: Justin Chen <mail@justin0u0.com>
…uxt-content#504) setTimeout(100) could fire before draftDocuments.load() finished, causing selectByFsPath to fall back to stale SQLite content and drop IndexedDB draft slots. Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
UIcon does not support a size prop; it was silently ignored. Remove it so the icons render at their default size, and drop the redundant size on ItemBadge. Co-authored-by: Hendrik Heil <hendrik@heil.sh>
…d bodies (nuxt-content#515) * test(document): reproduce component array prop round-trip conflict Add two intentionally-failing tests showing that a component's array prop, in the body shape @nuxt/content (@nuxtjs/mdc) produces — a colon-bound, JSON-stringified `:prop` — is not round-tripped back to YAML frontmatter by contentFromDocument. Instead it is emitted as a malformed inline attribute (unescaped nested quotes), which surfaces as a permanent, un-resolvable conflict against the source file in the Studio editor. Covers both arrays of primitives and arrays of objects; the serializer breaks both identically. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * fix(document): round-trip ':key' JSON array/object props as YAML blocks @nuxtjs/mdc encodes a component's YAML-block array/object prop as a colon-bound, JSON-stringified attr ({ ':bar': '["baz"]' }). Two paths let that artifact survive to comark's renderer, which then emitted it as an inline string literal (::foo{:bar="[...]"}) instead of a YAML block — producing a permanent phantom conflict against the repo: - The MDC→Comark bridge unwrapped ':bar' to a real array, but the token-list collapse (for class/ping) then flattened primitive arrays back to space-joined scalars. Exempt ':'-unwrapped keys from collapse. - A body already in comark '.nodes' shape skips the bridge entirely, so the binding reached render untouched. Strip array/object ':'-bindings in contentFromMarkdownDocument via the new unbindComarkTree. Only array/object bindings are unwrapped; scalar bindings (:width="200", :reverse="true") are genuine comark inline bindings that round-trip on their own. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * test(document): cover ':key' prop round-trip branches Close the coverage gaps left by the fix: - legacy.ts bridge: a primitive array unwrapped from a ':' binding must survive as an array (not collapse to a space-joined string like a bare token-list array). This is the exact bug the collapse exemption fixed, previously unguarded. - render path: object prop renders as a YAML map block; a nested component's array prop is unwrapped (recursion); a scalar ':' binding (:width="200") stays an inline binding and is not unwrapped to a block. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> * docs(document): trim verbose comments to non-obvious why Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
… last location (nuxt-content#505) * fix(app): open file for current host route instead of forcing back to last location Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * refactor(app): source current route from host contract, not window.location open() read window.location.pathname to find the file for the current host page, but that includes the host app's baseURL and trailing-slash form, so it fails to match routePath under a non-root base. Route knowledge belongs to the host; expose it via host.app.getCurrentRoute() and let open() ask for it, matching how routeChange already flows route data across the boundary. Also drops the string overload (and its cast) from selectByRoute, since app.vue was its only string caller. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
…de}` wrapper (nuxt-content#517) comark renders a fence body from the `code` child and serializes any other `pre` attr as a `::pre{attr}` wrapper (comark >=0.4.0). A stray `code` attr on the `pre` therefore leaked as `::pre{code="…"}` instead of a plain ``` fence. Two paths put it there: - Editor (state D): `createCodeBlockElement` wrote `code` (plus stale Shiki `class`/`tabindex`) onto the `pre`. Build the attrs explicitly as `{language?, filename?}`; the raw source stays in the `code` child. - Stored original (state B): the `@nuxtjs/mdc` legacy bridge carried mdc's duplicated `props.code` straight onto the comark `pre`, so the conflict / formatting round-trip of `original.body` leaked the wrapper. `mdcToComark` now routes `pre` through `preMdcToComarkNode`, which moves `props.code` into the `code` child (the only intact copy in mdc's miscapture-repair path) and drops the attr. Scoped to `code` only — an authored class still round-trips as the canonical `::pre{.class}`, and comark drops the Shiki artifacts. Pin comark to pkg.pr.new@10ca6d9 (bare-`shiki` class guard) workspace-wide via overrides + `blockExoticSubdeps: false`, with the sha512 integrity computed from the tarball so frozen-lockfile installs don't fail with ERR_PNPM_MISSING_TARBALL_INTEGRITY. Adds round-trip tests for both paths asserting a plain ``` fence with no `::pre` wrapper.
…nuxt-content#516) comark's parse now supports `linkify: false`. Pass it at both parse sites so bare URLs in content aren't auto-converted to links at parse time — the editor already linkifies. Bumps comark to a build that exposes the option.
…ent#519) @nuxtjs/mdc keeps the fence's trailing newline in props.code, whereas comark's canonical code child omits it. The MDC→Comark bridge copied that newline into the tree, so the TipTap editor rendered an extra blank line at the end of every code block. Strip a single trailing newline in preMdcToComarkNode (the one point all pre nodes converge through). renderMarkdown output is byte-identical with or without it, so there's no round-trip or formatting-banner impact.
…xt-content#520) Pressing Backspace at the start of the block directly below the frontmatter node ran ProseMirror's default joinBackward, which deletes adjacent atoms in a branch that ignores `isolating`. The frontmatter is a content-less atom holding all its data in attrs, so the delete discarded that data; comarkToTiptap then re-inserted an empty frontmatter node, producing the "flash then reappears blank" behavior. Add a Backspace keymap that reads the selection and swallows the key when it would join into the frontmatter. The check is intentionally read-only and does NOT probe with joinBackward: inside a TipTap command, `state.tr` is the shared transaction TipTap auto-dispatches, so a probe would actually perform the deletion it was meant to detect.
…nuxt-content#521) Older Studio builds persisted legacy (MarkdownRoot/minimark) bodies in IndexedDB; comark's renderMarkdown expects a ComarkTree and threw on publish/render. Upgrade to ComarkTree at the read boundaries (host DB reads + draft load) via `ensureComarkBody` in the legacy compat layer.
…nt#524) comark renders a br node natively as a markdown hard break (' \n'). The ':br' override (added in the comark migration to mimic the old @nuxtjs/mdc output) no longer matches comark and corrupted output to ':br' — which the site renders as 'Unknown node: hard break' and which collides with the :br: emoji shortcode. Delete the override and defer to comark's native rendering. Refs: nuxt-content#265
…ection race (nuxt-content#530) The reselect-current-item branch in handleDraftUpdate ran unawaited, so it could resolve after a caller's own explicit selectItemByFsPath call (e.g. CreateDocument) and clobber currentItem/draft.current back to the previous item, bouncing the editor back to the file list and skipping the host navigateTo call.
…uxt-content#531) Stored node attrs may use whatever casing the source markdown/parser produced, while nuxt-component-meta always declares props in camelCase, so compare both forms via kebabCase to bind a component's declared prop to its stored value regardless of casing.
- Created a comprehensive guide for testing Vue 3 components, including examples for component tests, composables, and utilities. - Added TypeScript-specific patterns for Vue 3 development, covering provide/inject types, strict templates, and common mistakes. - Introduced client utilities documentation for formatting, validation, transformation, and parsing functions. - Set up a Cloudinary playground with Nuxt Studio, including configuration files, content structure, and media handling routes.
Keep uploaded Cloudinary media visible immediately in Studio and sync delete behavior without reload.
|
@narr07 is attempting to deploy a commit to the Nuxt Team on Vercel. A member of the Team first needs to authorize it. |
commit: |
|
Hi @narr07, this PR seems to combine a few different things and has a pretty messy history. Could you please split it up by feature and drop the already merged commits for an easier to read history? Thanks! |
Hi @hendrikheil, I have split the changes into two clean, separate pull requests as suggested:
Both PRs now have clean histories with exactly one commit each based on the latest upstream main. I am closing this PR now. Thanks again for your guidance! |
Summary
This PR introduces Cloudinary as an external media provider and expands AI content assistance capabilities by supporting GroqCloud and Google Gemini providers alongside Vercel AI Gateway.
Features Added
🖼️ Cloudinary External Media Provider
cloudinaryprovider option understudio.mediaconfiguration.🤖 Multi-Provider AI Support (Groq & Gemini)
GROQ_API_KEY) offering free Llama 3.3, Llama 3.1, Gemma & Qwen models.GEMINI_API_KEY/NUXT_STUDIO_AI_GEMINI_API_KEY).provider,groqModel,groqFastModel,geminiModel, andgeminiFastModelinnuxt.config.ts.Verification & Tests
upstream/main.0 errors).vue-tsc -p src/app/tsconfig.app.json).385/385 tests passing).