diff --git a/src/lib/components/admin/composer/BubbleColorPicker.svelte b/src/lib/components/admin/composer/BubbleColorPicker.svelte
index d62ca63..e5954f6 100644
--- a/src/lib/components/admin/composer/BubbleColorPicker.svelte
+++ b/src/lib/components/admin/composer/BubbleColorPicker.svelte
@@ -36,7 +36,7 @@
'#FFC107', // Amber
'#FF9800', // Orange
'#FF5722', // Deep Orange
- '#795548' // Brown
+ '#795548' // Brown
]
// Lighter, pastel colors for highlighting
@@ -60,7 +60,7 @@
'#FFE0B2', // Light Orange
'#FFCCBC', // Light Deep Orange
'#D7CCC8', // Light Brown
- '#F5F5F5' // Light Gray
+ '#F5F5F5' // Light Gray
]
const presetColors = $derived(mode === 'text' ? textPresetColors : highlightPresetColors)
@@ -123,9 +123,7 @@
@@ -141,7 +139,7 @@
{#if !showPicker}
-
{/if}
@@ -335,7 +331,7 @@
:global(.bubble-color-picker .input) {
margin-top: 8px;
-
+
input {
width: 100%;
padding: 6px 10px;
@@ -354,4 +350,4 @@
}
}
}
-
\ No newline at end of file
+
diff --git a/src/lib/components/admin/composer/BubbleTextStyleMenu.svelte b/src/lib/components/admin/composer/BubbleTextStyleMenu.svelte
index 4e2c341..1009f35 100644
--- a/src/lib/components/admin/composer/BubbleTextStyleMenu.svelte
+++ b/src/lib/components/admin/composer/BubbleTextStyleMenu.svelte
@@ -1,6 +1,6 @@
-
- Insert An Audio
+ Insert audio
+
+ {#if showPane}
+ paneManager.close()}
+ {deleteNode}
+ {albumId}
+ />
+ {/if}
\ No newline at end of file
+
+ .location-form {
+ display: flex;
+ flex-direction: column;
+ gap: $unit-2x;
+ }
+
+ .form-group {
+ display: flex;
+ flex-direction: column;
+ gap: $unit-half;
+ }
+
+ .form-label {
+ font-size: $font-size-extra-small;
+ font-weight: 500;
+ color: $gray-30;
+ }
+
+ .form-input,
+ .form-textarea {
+ padding: $unit $unit-2x;
+ border: 1px solid $gray-85;
+ border-radius: $corner-radius-sm;
+ font-size: $font-size-small;
+ background: $white;
+ font-family: inherit;
+
+ &:focus {
+ outline: none;
+ border-color: $primary-color;
+ }
+ }
+
+ .form-textarea {
+ resize: vertical;
+ min-height: 60px;
+ }
+
+ .coordinates-group {
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+ gap: $unit-2x;
+ }
+
+ .location-options {
+ display: flex;
+ gap: $unit-3x;
+ align-items: center;
+ }
+
+ .option-label {
+ display: flex;
+ align-items: center;
+ gap: $unit;
+ font-size: $font-size-extra-small;
+ font-weight: 500;
+ color: $gray-30;
+ }
+
+ .color-input {
+ width: 36px;
+ height: 24px;
+ padding: 0;
+ border: 1px solid $gray-85;
+ border-radius: $corner-radius-sm;
+ cursor: pointer;
+ }
+
+ .zoom-input {
+ width: 100px;
+ }
+
+ .submit-btn {
+ width: 100%;
+ padding: $unit-2x;
+ background: $primary-color;
+ color: $white;
+ border: none;
+ border-radius: $corner-radius-sm;
+ font-size: $font-size-small;
+ font-weight: 500;
+ cursor: pointer;
+ transition: all 0.15s ease;
+
+ &:hover:not(:disabled) {
+ background: darken($primary-color, 10%);
+ }
+
+ &:disabled {
+ opacity: 0.5;
+ cursor: not-allowed;
+ }
+ }
+
+ .required {
+ color: $red-60;
+ }
+
diff --git a/src/lib/components/edra/headless/components/EnhancedImagePlaceholder.svelte b/src/lib/components/edra/headless/components/EnhancedImagePlaceholder.svelte
index 00799a4..9d4016f 100644
--- a/src/lib/components/edra/headless/components/EnhancedImagePlaceholder.svelte
+++ b/src/lib/components/edra/headless/components/EnhancedImagePlaceholder.svelte
@@ -4,6 +4,7 @@
import { NodeViewWrapper } from 'svelte-tiptap'
import { getContext } from 'svelte'
import ContentInsertionPane from './ContentInsertionPane.svelte'
+ import { paneManager } from '$lib/stores/pane-manager'
const { editor, deleteNode, getPos }: NodeViewProps = $props()
@@ -11,20 +12,29 @@
const editorContext = getContext
('editorContext') || {}
const albumId = $derived(editorContext.albumId)
+ // Generate unique pane ID based on node position
+ const paneId = $derived(`image-placeholder-${getPos?.() ?? Math.random()}`)
+
let showPane = $state(false)
let panePosition = $state({ x: 0, y: 0 })
+ // Subscribe to pane manager
+ const paneState = $derived($paneManager)
+ $effect(() => {
+ showPane = paneManager.isActive(paneId, paneState)
+ })
+
function handleClick(e: MouseEvent) {
if (!editor.isEditable) return
e.preventDefault()
-
+
// Get position for pane
const rect = (e.currentTarget as HTMLElement).getBoundingClientRect()
panePosition = {
x: rect.left,
y: rect.bottom + 8
}
- showPane = true
+ paneManager.open(paneId)
}
// Handle keyboard navigation
@@ -34,7 +44,7 @@
handleClick(e as any)
} else if (e.key === 'Escape') {
if (showPane) {
- showPane = false
+ paneManager.close()
} else {
deleteNode()
}
@@ -53,12 +63,13 @@
Insert an image
-
+
{#if showPane}
showPane = false}
+ contentType="image"
+ onClose={() => paneManager.close()}
{deleteNode}
{albumId}
/>
diff --git a/src/lib/components/edra/headless/components/GalleryPlaceholder.svelte b/src/lib/components/edra/headless/components/GalleryPlaceholder.svelte
index bafd55e..4d3d6c5 100644
--- a/src/lib/components/edra/headless/components/GalleryPlaceholder.svelte
+++ b/src/lib/components/edra/headless/components/GalleryPlaceholder.svelte
@@ -4,27 +4,37 @@
import { NodeViewWrapper } from 'svelte-tiptap'
import { getContext } from 'svelte'
import ContentInsertionPane from './ContentInsertionPane.svelte'
+ import { paneManager } from '$lib/stores/pane-manager'
- const { editor, deleteNode }: NodeViewProps = $props()
+ const { editor, deleteNode, getPos }: NodeViewProps = $props()
// Get album context if available
const editorContext = getContext('editorContext') || {}
const albumId = $derived(editorContext.albumId)
+ // Generate unique pane ID based on node position
+ const paneId = $derived(`gallery-${getPos?.() ?? Math.random()}`)
+
let showPane = $state(false)
let panePosition = $state({ x: 0, y: 0 })
+ // Subscribe to pane manager
+ const paneState = $derived($paneManager)
+ $effect(() => {
+ showPane = paneManager.isActive(paneId, paneState)
+ })
+
function handleClick(e: MouseEvent) {
if (!editor.isEditable) return
e.preventDefault()
-
+
// Get position for pane
const rect = (e.currentTarget as HTMLElement).getBoundingClientRect()
panePosition = {
x: rect.left,
y: rect.bottom + 8
}
- showPane = true
+ paneManager.open(paneId)
}
// Handle keyboard navigation
@@ -34,7 +44,7 @@
handleClick(e as any)
} else if (e.key === 'Escape') {
if (showPane) {
- showPane = false
+ paneManager.close()
} else {
deleteNode()
}
@@ -53,12 +63,13 @@
Insert a gallery
-
+
{#if showPane}
showPane = false}
+ contentType="gallery"
+ onClose={() => paneManager.close()}
{deleteNode}
{albumId}
/>
diff --git a/src/lib/components/edra/headless/components/GeolocationPlaceholder.svelte b/src/lib/components/edra/headless/components/GeolocationPlaceholder.svelte
index 1341795..0fa75b3 100644
--- a/src/lib/components/edra/headless/components/GeolocationPlaceholder.svelte
+++ b/src/lib/components/edra/headless/components/GeolocationPlaceholder.svelte
@@ -1,257 +1,118 @@
-
-
-
-
-
+
+
+
+ Insert location
+
- {#if !isConfigured}
-
-
Add Location
-
Add a map with a location marker
-
(isConfigured = true)}>
- Configure Location
-
-
- {:else}
-
- {/if}
-
+ {#if showPane}
+ paneManager.close()}
+ {deleteNode}
+ {albumId}
+ />
+ {/if}
diff --git a/src/lib/components/edra/headless/components/IFramePlaceholder.svelte b/src/lib/components/edra/headless/components/IFramePlaceholder.svelte
index a27310e..a2feaae 100644
--- a/src/lib/components/edra/headless/components/IFramePlaceholder.svelte
+++ b/src/lib/components/edra/headless/components/IFramePlaceholder.svelte
@@ -2,30 +2,78 @@
import type { NodeViewProps } from '@tiptap/core'
import CodeXML from 'lucide-svelte/icons/code-xml'
import { NodeViewWrapper } from 'svelte-tiptap'
- const { editor }: NodeViewProps = $props()
+ import { getContext } from 'svelte'
+ import ContentInsertionPane from './ContentInsertionPane.svelte'
+ import { paneManager } from '$lib/stores/pane-manager'
+
+ const { editor, deleteNode, getPos }: NodeViewProps = $props()
+
+ // Get album context if available
+ const editorContext = getContext('editorContext') || {}
+ const albumId = $derived(editorContext.albumId)
+
+ // Generate unique pane ID based on node position
+ const paneId = $derived(`iframe-${getPos?.() ?? Math.random()}`)
+
+ let showPane = $state(false)
+ let panePosition = $state({ x: 0, y: 0 })
+
+ // Subscribe to pane manager
+ const paneState = $derived($paneManager)
+ $effect(() => {
+ showPane = paneManager.isActive(paneId, paneState)
+ })
function handleClick(e: MouseEvent) {
if (!editor.isEditable) return
e.preventDefault()
- const iFrameURL = prompt('Enter the URL of an iFrame:')
- if (!iFrameURL) {
- return
+
+ // Get position for pane
+ const rect = (e.currentTarget as HTMLElement).getBoundingClientRect()
+ panePosition = {
+ x: rect.left,
+ y: rect.bottom + 8
+ }
+ paneManager.open(paneId)
+ }
+
+ // Handle keyboard navigation
+ function handleKeyDown(e: KeyboardEvent) {
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault()
+ handleClick(e as any)
+ } else if (e.key === 'Escape') {
+ if (showPane) {
+ paneManager.close()
+ } else {
+ deleteNode()
+ }
}
- editor.chain().focus().setIframe({ src: iFrameURL }).run()
}
-
- Insert An IFrame
+ Insert embed
+
+ {#if showPane}
+ paneManager.close()}
+ {deleteNode}
+ {albumId}
+ />
+ {/if}
\ No newline at end of file
+
diff --git a/src/lib/components/edra/headless/components/UrlEmbedPlaceholder.svelte b/src/lib/components/edra/headless/components/UrlEmbedPlaceholder.svelte
index 2fe1062..b964bfd 100644
--- a/src/lib/components/edra/headless/components/UrlEmbedPlaceholder.svelte
+++ b/src/lib/components/edra/headless/components/UrlEmbedPlaceholder.svelte
@@ -1,279 +1,118 @@
-
- {#if showInput && !node.attrs.url}
-
-
-
- Embed
-
-
- {:else if loading}
-
-
- Loading preview...
-
- {:else if error}
-
-
-
- {errorMessage}
- {
- showInput = true
- error = false
- }}
- class="retry-button"
- >
- Try another URL
-
-
-
- {:else}
-
-
-
- Embed a link
-
+
+
+
+ Embed a link
+
+
+ {#if showPane}
+ paneManager.close()}
+ {deleteNode}
+ {albumId}
+ />
{/if}
diff --git a/src/lib/components/edra/headless/components/VideoPlaceholder.svelte b/src/lib/components/edra/headless/components/VideoPlaceholder.svelte
index a12fb84..abebc6e 100644
--- a/src/lib/components/edra/headless/components/VideoPlaceholder.svelte
+++ b/src/lib/components/edra/headless/components/VideoPlaceholder.svelte
@@ -2,30 +2,78 @@
import type { NodeViewProps } from '@tiptap/core'
import Video from 'lucide-svelte/icons/video'
import { NodeViewWrapper } from 'svelte-tiptap'
- const { editor }: NodeViewProps = $props()
+ import { getContext } from 'svelte'
+ import ContentInsertionPane from './ContentInsertionPane.svelte'
+ import { paneManager } from '$lib/stores/pane-manager'
+
+ const { editor, deleteNode, getPos }: NodeViewProps = $props()
+
+ // Get album context if available
+ const editorContext = getContext('editorContext') || {}
+ const albumId = $derived(editorContext.albumId)
+
+ // Generate unique pane ID based on node position
+ const paneId = $derived(`video-${getPos?.() ?? Math.random()}`)
+
+ let showPane = $state(false)
+ let panePosition = $state({ x: 0, y: 0 })
+
+ // Subscribe to pane manager
+ const paneState = $derived($paneManager)
+ $effect(() => {
+ showPane = paneManager.isActive(paneId, paneState)
+ })
function handleClick(e: MouseEvent) {
if (!editor.isEditable) return
e.preventDefault()
- const videoUrl = prompt('Enter the URL of the video:')
- if (!videoUrl) {
- return
+
+ // Get position for pane
+ const rect = (e.currentTarget as HTMLElement).getBoundingClientRect()
+ panePosition = {
+ x: rect.left,
+ y: rect.bottom + 8
+ }
+ paneManager.open(paneId)
+ }
+
+ // Handle keyboard navigation
+ function handleKeyDown(e: KeyboardEvent) {
+ if (e.key === 'Enter' || e.key === ' ') {
+ e.preventDefault()
+ handleClick(e as any)
+ } else if (e.key === 'Escape') {
+ if (showPane) {
+ paneManager.close()
+ } else {
+ deleteNode()
+ }
}
- editor.chain().focus().setVideo(videoUrl).run()
}
-
- Insert A Video
+ Insert video
+
+ {#if showPane}
+ paneManager.close()}
+ {deleteNode}
+ {albumId}
+ />
+ {/if}