Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@
},
"resolutions": {
"@nuxt/content": "https://pkg.pr.new/@nuxt/content@9b4b4f2",
"nuxt-component-meta": "https://pkg.pr.new/nuxt-component-meta@104",
"nuxt-component-meta": "https://pkg.pr.new/nuxt-component-meta@b65b98e",
"@vueuse/core": "13.9.0"
},
"packageManager": "[email protected]",
Expand Down
14 changes: 7 additions & 7 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions src/app/src/components/content/ContentEditorTipTap.vue
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { InlineElement } from '../../utils/tiptap/extensions/inline-element'
import { SpanStyle } from '../../utils/tiptap/extensions/span-style'
import { compressTree } from '@nuxt/content/runtime'
import TiptapSpanStylePopover from '../tiptap/TiptapSpanStylePopover.vue'
import { Binding } from '../../utils/tiptap/extensions/binding'

const props = defineProps({
draftItem: {
Expand Down Expand Up @@ -203,6 +204,7 @@ const emojiItems: EditorEmojiMenuItem[] = gitHubEmojis.filter(
Slot,
CodeBlock,
Emoji,
Binding,
]"
:placeholder="$t('studio.tiptap.editor.placeholder')"
>
Expand Down
1 change: 1 addition & 0 deletions src/app/src/components/form/FormPanelInput.vue
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,7 @@ function computeValue(formItem: FormItem): unknown {
v-model="(model as string | number)"
:placeholder="placeholder"
:type="inputType"
size="xs"
class="w-full"
/>
</UFormField>
Expand Down
1 change: 1 addition & 0 deletions src/app/src/components/form/input/FormInputObject.vue
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ function updateValue(key: string, value: string | number) {
>
<UInput
v-model="(entry.value as string | number)"
size="xs"
:placeholder="entry.placeholder"
:type="entry.type"
class="w-full"
Expand Down
2 changes: 1 addition & 1 deletion src/app/src/components/shared/Collapsible.vue
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ const open = defineModel('open', { type: Boolean, default: undefined })
>
<div class="flex items-center justify-between gap-2 w-full">
<div class="flex items-center gap-2">
<div class="flex items-center justify-center size-4 rounded bg-gray-100 dark:bg-gray-800 transition-colors duration-200 group-hover/collapsible:bg-gray-200 dark:group-hover/collapsible:bg-gray-700">
<div class="flex items-center justify-center size-3.5 rounded bg-gray-100 dark:bg-gray-800 transition-colors duration-200 group-hover/collapsible:bg-gray-200 dark:group-hover/collapsible:bg-gray-700">
<UIcon
name="i-lucide-chevron-right"
class="size-2.5 text-gray-500 dark:text-gray-400 transition-transform duration-200 group-data-[state=open]/collapsible:rotate-90"
Expand Down
3 changes: 2 additions & 1 deletion src/app/src/components/shared/item/ItemActionsToolbar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,11 @@ import { useI18n } from 'vue-i18n'
const { context, gitProvider } = useStudio()
const { preferences, updatePreference } = useStudioState()
const { t } = useI18n()
const fileInputRef = ref<HTMLInputElement>()

// @ts-expect-error vue-tsc error in cli
const toolbarRef = ref<HTMLElement>()
const fileInputRef = ref<HTMLInputElement>()

const pendingAction = ref<StudioAction<StudioItemActionId> | null>(null)
const loadingAction = ref<StudioAction<StudioItemActionId> | null>(null)

Expand Down
22 changes: 15 additions & 7 deletions src/app/src/components/tiptap/TiptapComponentProps.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,10 @@ const props = defineProps({
type: Function as PropType<(props: Record<string, unknown>) => void>,
required: true,
},
hideTitle: {
type: Boolean,
default: false,
},
})

const { host } = useStudio()
Expand Down Expand Up @@ -119,11 +123,14 @@ function normalizePropsTree(tree: FormTree): FormTree {

<template>
<div
class="p-4 min-w-[400px] max-w-[500px] not-prose overflow-y-auto max-h-[400px] relative"
class="p-3 min-w-[400px] max-w-[500px] not-prose overflow-y-auto max-h-[400px] relative"
@click.stop
>
<!-- Header -->
<div class="text-sm font-mono font-semibold text-highlighted mb-4">
<div
v-if="!hideTitle"
class="text-sm font-mono font-semibold text-highlighted mb-2"
>
{{ titleCase(componentName).replace(/^U /, '') }} properties
</div>

Expand All @@ -145,8 +152,8 @@ function normalizePropsTree(tree: FormTree): FormTree {
<div class="w-2/3 flex items-center gap-2">
<!-- Nested form overlay for arrays/objects -->
<template v-if="nestedForm?.key === prop.key">
<div class="fixed inset-0 bg-default z-50 flex flex-col p-4 overflow-y-auto rounded-lg">
<div class="flex items-center justify-between mb-4">
<div class="fixed inset-0 bg-default z-50 flex flex-col p-3 overflow-y-auto rounded-lg">
<div class="flex items-center justify-between mb-2">
<span class="text-sm font-mono font-semibold text-highlighted">
{{ prop.title }}
</span>
Expand Down Expand Up @@ -202,6 +209,7 @@ function normalizePropsTree(tree: FormTree): FormTree {
<USwitch
:model-value="Boolean(prop.value)"
:disabled="prop.disabled"
size="xs"
@update:model-value="updateProp(key, $event)"
/>
</template>
Expand All @@ -213,7 +221,7 @@ function normalizePropsTree(tree: FormTree): FormTree {
:items="prop.options"
:disabled="prop.disabled"
class="w-full"
size="sm"
size="xs"
@update:model-value="updateProp(key, $event)"
/>
</template>
Expand All @@ -226,7 +234,7 @@ function normalizePropsTree(tree: FormTree): FormTree {
:placeholder="getPlaceholder(prop)"
:disabled="prop.disabled"
class="w-full"
size="sm"
size="xs"
@update:model-value="updateProp(key, $event)"
/>
</template>
Expand All @@ -238,7 +246,7 @@ function normalizePropsTree(tree: FormTree): FormTree {
:placeholder="getPlaceholder(prop)"
:disabled="prop.disabled"
class="w-full"
size="sm"
size="xs"
@update:model-value="updateProp(key, $event)"
/>
</template>
Expand Down
6 changes: 3 additions & 3 deletions src/app/src/components/tiptap/TiptapLinkPopover.vue
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ function handleKeyDown(event: KeyboardEvent) {
<UButton
icon="i-lucide-corner-down-left"
variant="ghost"
size="sm"
size="xs"
:disabled="!url && !active"
:title="$t('studio.tiptap.link.applyLink')"
@click="setLink"
Expand All @@ -141,7 +141,7 @@ function handleKeyDown(event: KeyboardEvent) {
icon="i-lucide-external-link"
color="neutral"
variant="ghost"
size="sm"
size="xs"
:disabled="!url && !active"
:title="$t('studio.tiptap.link.openInNewWindow')"
@click="openLink"
Expand All @@ -151,7 +151,7 @@ function handleKeyDown(event: KeyboardEvent) {
icon="i-lucide-trash"
color="neutral"
variant="ghost"
size="sm"
size="xs"
:disabled="!url && !active"
:title="$t('studio.tiptap.link.removeLink')"
@click="removeLink"
Expand Down
4 changes: 2 additions & 2 deletions src/app/src/components/tiptap/TiptapSpanStylePopover.vue
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ function handleKeyDown(event: KeyboardEvent) {
<UButton
icon="i-lucide-corner-down-left"
variant="ghost"
size="sm"
size="xs"
@click="applySpanStyle"
/>
</UTooltip>
Expand All @@ -130,7 +130,7 @@ function handleKeyDown(event: KeyboardEvent) {
icon="i-lucide-trash"
color="neutral"
variant="ghost"
size="sm"
size="xs"
:disabled="!active"
@click="removeSpanStyle"
/>
Expand Down
119 changes: 119 additions & 0 deletions src/app/src/components/tiptap/extension/TiptapExtensionBinding.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { NodeViewWrapper, NodeViewContent, nodeViewProps } from '@tiptap/vue-3'
import { useI18n } from 'vue-i18n'

const props = defineProps(nodeViewProps)
const { t } = useI18n()

const isInside = computed(() => props.selected)
const label = computed(() => props.node.attrs.value || props.node.attrs.defaultValue || '')

const isPopoverOpen = ref(false)
const valueAttr = ref('')
const defaultValueAttr = ref('')

watch(
() => [props.node.attrs.value, props.node.attrs.defaultValue],
() => syncAttributes(),
{ immediate: true },
)

function syncAttributes() {
valueAttr.value = props.node.attrs.value || ''
defaultValueAttr.value = props.node.attrs.defaultValue || ''
}

function applyBinding() {
const attrs = {
value: valueAttr.value.trim() || undefined,
defaultValue: defaultValueAttr.value.trim() || undefined,
}
props.updateAttributes(attrs)
isPopoverOpen.value = false
}

function removeBinding() {
props.editor.chain().focus().unsetBinding().run()
valueAttr.value = ''
defaultValueAttr.value = ''
isPopoverOpen.value = false
}

function handleKeyDown(event: KeyboardEvent) {
if (event.key === 'Enter') {
event.preventDefault()
applyBinding()
}
}
</script>

<template>
<NodeViewWrapper
as="span"
:contenteditable="false"
>
<div
class="group inline-flex items-center gap-1 border border-default rounded-md text-muted px-2 mx-0.5 hover:bg-muted transition-colors cursor-pointer"
:class="{ 'ring-1 ring-primary/60': isInside }"
@click="isPopoverOpen = true"
>
<UIcon
name="i-lucide-variable"
class="size-3 shrink-0 text-muted group-hover:text-default"
:class="{ 'text-default': isPopoverOpen }"
/>
<NodeViewContent
class="text-sm text-default truncate! max-w-40"
as="span"
/>
<span
v-if="!props.node.textContent"
class="text-xs text-muted"
>
{{ label || 'binding' }}
</span>
</div>

<UPopover v-model:open="isPopoverOpen">
<span />
<template #content>
<div class="flex flex-col gap-0.5 w-64 p-1">
<UInput
v-model="valueAttr"
autofocus
variant="none"
name="value"
leading-icon="i-lucide-braces"
:placeholder="t('studio.tiptap.binding.variablePlaceholder')"
@keydown="handleKeyDown"
/>
<UInput
v-model="defaultValueAttr"
variant="none"
name="defaultValue"
leading-icon="i-lucide-text-cursor-input"
:placeholder="t('studio.tiptap.binding.defaultValuePlaceholder')"
@keydown="handleKeyDown"
/>
<USeparator />
<div class="flex items-center justify-end gap-0.5 px-1 py-0.5">
<UButton
icon="i-lucide-corner-down-left"
variant="ghost"
size="xs"
@click="applyBinding"
/>
<UButton
icon="i-lucide-trash"
color="neutral"
variant="ghost"
size="xs"
@click="removeBinding"
/>
</div>
</div>
</template>
</UPopover>
</NodeViewWrapper>
</template>
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ function updateComponentProps(props: Record<string, unknown>) {
variant="ghost"
size="2xs"
class="text-muted hover:text-default"
icon="i-lucide-settings"
icon="i-lucide-sliders-horizontal"
:disabled="!isEditable"
:aria-label="$t('studio.tiptap.element.editProps')"
@click.stop
Expand Down
Loading
Loading