fix(workspace): English slash commands, short texts, wider menu
/image replaces /bild (aliases bild/foto keep working), shorter descriptions for /link, /image, /audio, /diktat; the menu is wider, the command never wraps and long descriptions are cut off. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
1f63e08a73
commit
1632dd22c4
3 files changed
+17
-13
No files matched your search
@@ -200,7 +200,7 @@ export const HelpView: React.FC<HelpViewProps> = ({ onBack }) => {
|
||||
</div>
|
||||
<div className="p-1.5 bg-panel rounded border border-border flex items-center gap-1.5 text-main col-span-2">
|
||||
<span className="text-accent font-bold">🖼️</span>
|
||||
<span>/bild (Bild hochladen, fotografieren, einfügen oder per Link; nur im leeren Punkt)</span>
|
||||
<span>/image oder /bild (Bild hochladen, fotografieren, einfügen oder per Link; nur im leeren Punkt)</span>
|
||||
</div>
|
||||
<div className="p-1.5 bg-panel rounded border border-border flex items-center gap-1.5 text-main col-span-2">
|
||||
<span className="text-accent font-bold">🎙️</span>
|
||||
|
||||
@@ -8,7 +8,7 @@ interface ImagePickerProps {
|
||||
onCancel: () => void;
|
||||
}
|
||||
|
||||
// Inline box an empty bullet turns into after /bild: pick a file (or the camera on phones), drop or
|
||||
// Inline box an empty bullet turns into after /image: pick a file (or the camera on phones), drop or
|
||||
// paste an image, or give a link the server copies. Nothing is synced until the upload succeeded.
|
||||
export const ImagePicker: React.FC<ImagePickerProps> = ({ docId, onDone, onCancel }) => {
|
||||
const [busy, setBusy] = useState(false);
|
||||
|
||||
@@ -45,20 +45,22 @@ export interface SlashCommand {
|
||||
icon: React.ReactNode;
|
||||
/** Only offered in an empty bullet; the bullet turns into an inline picker first */
|
||||
media?: boolean;
|
||||
/** Further names that find the command (e.g. German ones) */
|
||||
aliases?: string[];
|
||||
}
|
||||
|
||||
const SLASH_COMMANDS: SlashCommand[] = [
|
||||
{ type: 'todo', label: '/todo', desc: 'Aufgabe mit Checkbox', icon: <ListTodo className="w-3.5 h-3.5" /> },
|
||||
{ type: 'link', label: '/link', desc: 'Notiz verknüpfen oder Web-Link einfügen', icon: <LinkIcon className="w-3.5 h-3.5" /> },
|
||||
{ type: 'link', label: '/link', desc: 'Notiz oder Weblink', icon: <LinkIcon className="w-3.5 h-3.5" /> },
|
||||
{ type: 'h1', label: '/h1', desc: 'Große Überschrift', icon: <Heading1 className="w-3.5 h-3.5" /> },
|
||||
{ type: 'h2', label: '/h2', desc: 'Abschnitts-Überschrift', icon: <Heading2 className="w-3.5 h-3.5" /> },
|
||||
{ type: 'h3', label: '/h3', desc: 'Unterabschnitt', icon: <Heading3 className="w-3.5 h-3.5" /> },
|
||||
{ type: 'code', label: '/code', desc: 'Code-Block mit Syntax', icon: <Code className="w-3.5 h-3.5" /> },
|
||||
{ type: 'callout', label: '/callout', desc: 'Hinweisbox', icon: <MessageSquare className="w-3.5 h-3.5" /> },
|
||||
{ type: 'divider', label: '/divider', desc: 'Trennlinie', icon: <Minus className="w-3.5 h-3.5" /> },
|
||||
{ type: 'image', label: '/bild', desc: 'Bild hochladen, fotografieren oder verlinken (image)', icon: <ImageIcon className="w-3.5 h-3.5" />, media: true },
|
||||
{ type: 'audio', label: '/audio', desc: 'Sprachnotiz aufnehmen oder Audiodatei hochladen', icon: <Mic className="w-3.5 h-3.5" />, media: true },
|
||||
{ type: 'dictation', label: '/diktat', desc: 'Sprechen statt tippen: Text direkt in den Punkt', icon: <AudioLines className="w-3.5 h-3.5" />, media: true },
|
||||
{ type: 'image', label: '/image', desc: 'Bilder hochladen oder verlinken', icon: <ImageIcon className="w-3.5 h-3.5" />, media: true, aliases: ['bild', 'foto'] },
|
||||
{ type: 'audio', label: '/audio', desc: 'Sprachnotiz oder Audiodatei', icon: <Mic className="w-3.5 h-3.5" />, media: true, aliases: ['sprachnotiz'] },
|
||||
{ type: 'dictation', label: '/diktat', desc: 'Sprechen statt tippen', icon: <AudioLines className="w-3.5 h-3.5" />, media: true, aliases: ['dictate'] },
|
||||
];
|
||||
|
||||
export interface OutlinerBlockProps {
|
||||
@@ -149,7 +151,7 @@ export const OutlinerBlock: React.FC<OutlinerBlockProps> = ({
|
||||
// A block someone else is editing can still get the cursor (read-only), e.g. to press Enter at its end
|
||||
const canFocus = canEdit || (!!lockedBy && !!canAppendBelow);
|
||||
const [slashSearch, setSlashSearch] = useState<string | null>(null);
|
||||
// Inline picker an empty bullet turns into after /bild (local only, nothing synced until done)
|
||||
// Inline picker an empty bullet turns into after /image (local only, nothing synced until done)
|
||||
const [mediaPicker, setMediaPicker] = useState<'image' | 'audio' | 'dictation' | null>(null);
|
||||
const [slashIndex, setSlashIndex] = useState(0);
|
||||
const [showLinkModal, setShowLinkModal] = useState<boolean>(false);
|
||||
@@ -352,7 +354,9 @@ export const OutlinerBlock: React.FC<OutlinerBlockProps> = ({
|
||||
(c) =>
|
||||
(!c.media || emptyBullet) &&
|
||||
(c.type !== 'dictation' || ['server', 'browser'].includes(currentVoiceConfig().mode)) &&
|
||||
(c.label.toLowerCase().includes(cmdQuery) || c.desc.toLowerCase().includes(cmdQuery))
|
||||
(c.label.toLowerCase().includes(cmdQuery) ||
|
||||
c.desc.toLowerCase().includes(cmdQuery) ||
|
||||
(c.aliases || []).some((a) => a.includes(cmdQuery)))
|
||||
);
|
||||
}, [slashSearch, localContent]);
|
||||
|
||||
@@ -366,7 +370,7 @@ export const OutlinerBlock: React.FC<OutlinerBlockProps> = ({
|
||||
return;
|
||||
}
|
||||
if (cmd.media) {
|
||||
// The typed "/bild" must not stay in the synced text; the block type changes only after the upload
|
||||
// The typed "/image" must not stay in the synced text; the block type changes only after the upload
|
||||
setLocalContent('');
|
||||
localContentRef.current = '';
|
||||
flushDebounce('');
|
||||
@@ -1208,7 +1212,7 @@ export const OutlinerBlock: React.FC<OutlinerBlockProps> = ({
|
||||
|
||||
{/* Floating Slash Commands Menu */}
|
||||
{slashSearch !== null && canEdit && (
|
||||
<div className="absolute left-0 top-8 z-40 w-56 panel-surface shadow-panel border border-border rounded-card p-1">
|
||||
<div className="absolute left-0 top-8 z-40 w-72 max-w-[calc(100vw-2rem)] panel-surface shadow-panel border border-border rounded-card p-1">
|
||||
<div className="px-2 py-1 text-[10px] font-bold uppercase tracking-wider text-faint border-b border-border mb-1">
|
||||
Befehl auswählen
|
||||
</div>
|
||||
@@ -1227,11 +1231,11 @@ export const OutlinerBlock: React.FC<OutlinerBlockProps> = ({
|
||||
: 'text-main hover:bg-panel-elevated'
|
||||
}`}
|
||||
>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<div className="flex items-center gap-1.5 shrink-0">
|
||||
{cmd.icon}
|
||||
<span className="font-semibold">{cmd.label}</span>
|
||||
<span className="font-semibold whitespace-nowrap">{cmd.label}</span>
|
||||
</div>
|
||||
<span className="text-[10px] opacity-75">{cmd.desc}</span>
|
||||
<span className="ml-3 min-w-0 truncate text-right text-[10px] opacity-75">{cmd.desc}</span>
|
||||
</button>
|
||||
))}
|
||||
{filteredSlashCommands.length === 0 && (
|
||||
|
||||
Reference in new issue
Block a user