Browse Source

correct advanced editor

imwald
Silberengel 1 day ago
parent
commit
54ffb17192
  1. 4
      docker-compose.dev.yml
  2. 2
      docker-compose.prod.yml
  3. 4
      docker-compose.yml
  4. 63
      src/components/AdvancedEventLab/AdvancedEventLabDialog.tsx
  5. 16
      src/components/ui/dropdown-menu.tsx

4
docker-compose.dev.yml

@ -6,6 +6,10 @@ services: @@ -6,6 +6,10 @@ services:
dockerfile: Dockerfile
args:
VITE_PROXY_SERVER: ${JUMBLE_PROXY_SERVER_URL:-http://localhost:8090}
# Same-origin paths; reverse-proxy in front of :8089 should forward /api/* (see PROXY_SETUP.md).
VITE_READ_ALOUD_TTS_URL: ${JUMBLE_READ_ALOUD_TTS_URL:-/api/piper-tts}
VITE_LANGUAGE_TOOL_URL: ${JUMBLE_LANGUAGE_TOOL_URL:-/api/languagetool}
VITE_TRANSLATE_URL: ${JUMBLE_TRANSLATE_URL:-/api/translate}
ports:
- "8089:80"
restart: unless-stopped

2
docker-compose.prod.yml

@ -10,7 +10,7 @@ @@ -10,7 +10,7 @@
# ProxyPassReverse /api/piper-tts http://127.0.0.1:9876/api/piper-tts
# ProxyPass / http://127.0.0.1:8089/
# so the browser hits same-origin /api/piper-tts → piper HTTP proxy (see services/piper-tts-proxy + PROXY_SETUP.md); /sites/ → OG proxy; else static SPA on 8089.
# VITE_PROXY_SERVER / VITE_READ_ALOUD_TTS_URL are baked at image build — see scripts/build-and-push-prod.sh
# VITE_PROXY_SERVER, VITE_READ_ALOUD_TTS_URL, VITE_LANGUAGE_TOOL_URL, VITE_TRANSLATE_URL are baked at image build — see scripts/build-and-push-prod.sh (docker compose here does not build the SPA).
#
# NIP-66 monitor: set NIP66_MONITOR_NSEC (and optionally NIP66_MONITOR_NPUB) in the host env or .env.
# - Cron service `jumble-nip66-monitor` (Imwald NIP-66 monitor image) uses NIP66_MONITOR_NSEC to publish 30166/10166; nsec never goes to the client.

4
docker-compose.yml

@ -6,6 +6,10 @@ services: @@ -6,6 +6,10 @@ services:
dockerfile: Dockerfile
args:
VITE_PROXY_SERVER: ${JUMBLE_PROXY_SERVER_URL:-http://localhost:8090}
# Same-origin paths; reverse-proxy in front of :8089 should forward /api/* (see PROXY_SETUP.md).
VITE_READ_ALOUD_TTS_URL: ${JUMBLE_READ_ALOUD_TTS_URL:-/api/piper-tts}
VITE_LANGUAGE_TOOL_URL: ${JUMBLE_LANGUAGE_TOOL_URL:-/api/languagetool}
VITE_TRANSLATE_URL: ${JUMBLE_TRANSLATE_URL:-/api/translate}
ports:
- "8089:80"
restart: unless-stopped

63
src/components/AdvancedEventLab/AdvancedEventLabDialog.tsx

@ -8,6 +8,7 @@ import { @@ -8,6 +8,7 @@ import {
DialogTitle
} from '@/components/ui/dialog'
import { Label } from '@/components/ui/label'
import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'
import {
Select,
SelectContent,
@ -245,6 +246,7 @@ export default function AdvancedEventLabDialog({ @@ -245,6 +246,7 @@ export default function AdvancedEventLabDialog({
const LAB_DRAFT_DEBOUNCE_MS = 500
const [previewDoc, setPreviewDoc] = useState('')
const [labBodyTab, setLabBodyTab] = useState<'edit' | 'preview'>('edit')
/** Stable while payload matches; avoids remounting the editor when the parent passes a new `initial` object reference. */
const labEditorMountFingerprint =
@ -276,6 +278,15 @@ export default function AdvancedEventLabDialog({ @@ -276,6 +278,15 @@ export default function AdvancedEventLabDialog({
}, PREVIEW_DEBOUNCE_MS)
}, [])
const flushPreviewDocNow = useCallback(() => {
if (previewDebounceTimerRef.current) {
clearTimeout(previewDebounceTimerRef.current)
previewDebounceTimerRef.current = null
}
const doc = markupView.current?.state.doc.toString() ?? sliceRef.current?.content ?? ''
setPreviewDoc(doc)
}, [])
useEffect(() => {
schedulePreviewUpdateRef.current = schedulePreviewUpdate
}, [schedulePreviewUpdate])
@ -340,6 +351,8 @@ export default function AdvancedEventLabDialog({ @@ -340,6 +351,8 @@ export default function AdvancedEventLabDialog({
previewDebounceTimerRef.current = null
}
setPreviewDoc('')
} else {
setLabBodyTab('edit')
}
}, [open])
@ -1019,28 +1032,46 @@ export default function AdvancedEventLabDialog({ @@ -1019,28 +1032,46 @@ export default function AdvancedEventLabDialog({
</div>
</div>
<div className="flex min-h-0 flex-1 flex-col overflow-y-auto overscroll-y-contain">
<AdvancedEventLabMarkupToolbar markupMode={markupMode} viewRef={markupView} sliceRef={sliceRef} />
<div className="flex min-h-0 flex-1 flex-col gap-0 px-4 py-2 max-lg:grid max-lg:grid-rows-[minmax(0,1fr)_minmax(0,1fr)] lg:flex lg:flex-row lg:py-2">
<div className="flex min-h-0 min-w-0 flex-col gap-2 overflow-hidden max-lg:min-h-0 lg:flex-1 lg:pr-3">
<h3 className="shrink-0 text-left text-sm font-semibold leading-none text-foreground">
<div className="flex min-h-0 flex-1 flex-col overflow-hidden overscroll-y-contain px-4 py-2">
<Tabs
value={labBodyTab}
onValueChange={(v) => {
const next = v as 'edit' | 'preview'
if (next === 'preview') flushPreviewDocNow()
setLabBodyTab(next)
}}
className="flex min-h-0 flex-1 flex-col gap-2"
>
<TabsList className="h-auto w-auto shrink-0 flex-wrap justify-start gap-1 p-1">
<TabsTrigger value="edit" className="shrink-0">
{t(
markupMode === 'asciidoc'
? 'Advanced lab markup label asciidoc'
: 'Advanced lab markup label markdown'
)}
</h3>
</TabsTrigger>
<TabsTrigger value="preview" className="shrink-0">
{t('Advanced lab preview')}
</TabsTrigger>
</TabsList>
<TabsContent
value="edit"
forceMount
className="mt-0 flex min-h-0 flex-1 flex-col gap-2 overflow-hidden data-[state=inactive]:hidden focus-visible:ring-0 focus-visible:ring-offset-0"
>
<AdvancedEventLabMarkupToolbar markupMode={markupMode} viewRef={markupView} sliceRef={sliceRef} />
<div
ref={markupHost}
className="flex min-h-0 flex-1 flex-col overflow-hidden rounded-md border bg-muted/20 lg:min-h-[min(42dvh,24rem)]"
className="flex min-h-0 flex-1 flex-col overflow-hidden rounded-md border bg-muted/20 min-h-[min(58dvh,32rem)]"
/>
</div>
<div className="flex min-h-0 min-w-0 flex-col gap-2 overflow-hidden -mx-4 border-t-2 border-border bg-muted/40 px-4 pb-3 pt-4 max-lg:min-h-0 max-lg:rounded-b-lg lg:mx-0 lg:mt-0 lg:flex-[0_1_42%] lg:max-w-[min(50%,40rem)] lg:rounded-none lg:border-t-0 lg:border-l lg:border-border lg:bg-transparent lg:px-0 lg:pb-0 lg:pt-0 lg:pl-3">
<h3 className="shrink-0 text-left text-sm font-semibold leading-none text-foreground">
{t('Advanced lab preview')}
</h3>
<div className="flex min-h-0 flex-1 overflow-y-auto rounded-md border border-border bg-background py-2 pl-0 pr-0 text-left lg:bg-muted/10 lg:px-2">
</TabsContent>
<TabsContent
value="preview"
className="mt-0 flex min-h-0 flex-1 flex-col overflow-hidden data-[state=inactive]:hidden focus-visible:ring-0 focus-visible:ring-offset-0"
>
<div className="flex min-h-0 flex-1 overflow-y-auto rounded-md border border-border bg-background py-2 text-left">
<AdvancedEventLabPreviewPane
markupMode={markupMode}
source={previewDoc}
@ -1048,8 +1079,8 @@ export default function AdvancedEventLabDialog({ @@ -1048,8 +1079,8 @@ export default function AdvancedEventLabDialog({
previewEmojiTags={mergedLabPreviewEmojiTags}
/>
</div>
</div>
</div>
</TabsContent>
</Tabs>
</div>
{formatToolbar ? (

16
src/components/ui/dropdown-menu.tsx

@ -143,14 +143,17 @@ const DropdownMenuSubContent = React.forwardRef< @@ -143,14 +143,17 @@ const DropdownMenuSubContent = React.forwardRef<
<div
ref={scrollAreaRef}
className={cn('p-1 popover-scroll-y', className)}
className={cn(
'p-1 popover-scroll-y max-h-[min(85dvh,calc(100dvh-3rem))] min-h-0 overflow-x-hidden',
className
)}
onScroll={checkScrollability}
>
{props.children}
</div>
{showScrollButtons && canScrollDown && (
<div className="absolute bottom-0 inset-x-0 flex items-center justify-center bg-popover">
<div className="absolute bottom-0 inset-x-0 z-10 flex items-center justify-center bg-popover">
<button
onClick={scrollDown}
onMouseEnter={scrollDown}
@ -227,7 +230,7 @@ const DropdownMenuContent = React.forwardRef< @@ -227,7 +230,7 @@ const DropdownMenuContent = React.forwardRef<
checkScrollability()
}
}}
collisionPadding={10}
collisionPadding={16}
{...props}
>
{showScrollButtons && canScrollUp && (
@ -245,14 +248,17 @@ const DropdownMenuContent = React.forwardRef< @@ -245,14 +248,17 @@ const DropdownMenuContent = React.forwardRef<
<div
ref={scrollAreaRef}
className={cn('p-1 popover-scroll-y', className)}
className={cn(
'p-1 popover-scroll-y max-h-[min(85dvh,calc(100dvh-3rem))] min-h-0 overflow-x-hidden',
className
)}
onScroll={checkScrollability}
>
{props.children}
</div>
{showScrollButtons && canScrollDown && (
<div className="absolute bottom-0 inset-x-0 flex items-center justify-center bg-popover">
<div className="absolute bottom-0 inset-x-0 z-10 flex items-center justify-center bg-popover">
<button
onClick={scrollDown}
onMouseEnter={scrollDown}

Loading…
Cancel
Save