feat(components): make CommentEditor configurable and use it for description

Replace the plain textarea in CreateReviewDialog with CommentEditor bound to
the description. The dialog now passes language, disabled, rows, ariaLabel,
previewLabel and placeholder so the description field gains markdown preview
and consistent accessible labels/placeholders.

Make CommentEditor props optional and configurable:
- onSend is now (() => void | Promise<void>) | undefined; Enter/Cmd+Enter and
  the send button are guarded/hidden when onSend is not provided.
- Add placeholder, ariaLabel, previewLabel and rows (default 5) to allow
  parent components to control appearance and accessibility.

Also add a small documentation tweak in commit_ai's cloud template: remind
authors to keep the title plain text and expand guidance on Markdown formatting.
This commit is contained in:
2026-09-18 12:53:43 +02:00
parent 910fac2626
commit ed484f5477
3 changed files with 26 additions and 7 deletions
+9 -5
View File
@@ -8,7 +8,11 @@
export let language: "de" | "en" = "en";
export let disabled = false;
export let busy = false;
export let onSend: () => void | Promise<void>;
export let onSend: (() => void | Promise<void>) | undefined = undefined;
export let placeholder: string | undefined = undefined;
export let ariaLabel: string | undefined = undefined;
export let previewLabel: string | undefined = undefined;
export let rows = 5;
let textarea: HTMLTextAreaElement;
let preview = false;
let monospace = false;
@@ -59,7 +63,7 @@
if (!(event.ctrlKey || event.metaKey)) return;
const key = event.key.toLowerCase();
if (key === "b" || key === "i" || key === "k") { event.preventDefault(); void format(key === "b" ? "**" : key === "i" ? "_" : "[",key === "b" ? "**" : key === "i" ? "_" : "](https://example.com)"); }
if (key === "enter") { event.preventDefault(); if (value.trim() && !busy && !disabled) void onSend(); }
if (key === "enter" && onSend) { event.preventDefault(); if (value.trim() && !busy && !disabled) void onSend(); }
}
function previewLinks(node: HTMLElement) {
node.addEventListener("click", previewClick);
@@ -87,14 +91,14 @@
</div>
{#if preview}
<!-- Rendered Markdown is restricted to safe content tags and sanitized before insertion. -->
<div class="md-preview" role="region" aria-label={de ? "Kommentarvorschau" : "Comment preview"} use:previewLinks>
<div class="md-preview" role="region" aria-label={previewLabel ?? (de ? "Kommentarvorschau" : "Comment preview")} use:previewLinks>
{#if value.trim()}{@html rendered}{:else}<span>{de ? "Noch nichts zum Anzeigen." : "Nothing to preview yet."}</span>{/if}
</div>
{:else}
<textarea bind:this={textarea} bind:value class:monospace wrap={wrap ? "soft" : "off"} maxlength="100000" rows="5" disabled={disabled || busy} aria-label={de ? "Kommentar schreiben" : "Write comment"} placeholder={de ? "Kommentar hinzufügen …" : "Leave a comment …"} onkeydown={keyboard}></textarea>
<textarea bind:this={textarea} bind:value class:monospace wrap={wrap ? "soft" : "off"} maxlength="100000" {rows} disabled={disabled || busy} aria-label={ariaLabel ?? (de ? "Kommentar schreiben" : "Write comment")} placeholder={placeholder ?? (de ? "Kommentar hinzufügen …" : "Leave a comment …")} onkeydown={keyboard}></textarea>
{/if}
{#if linkError}<small role="alert">{linkError}</small>{/if}
<div class="md-footer"><small>Markdown <span>· Ctrl/⌘ + Enter</span></small><button class="md-send" type="button" disabled={!value.trim() || disabled || busy} onclick={() => onSend()}><Send size={13} />{busy ? (de ? "Wird gesendet …" : "Sending …") : (de ? "Kommentar senden" : "Post comment")}</button></div>
<div class="md-footer"><small>Markdown {#if onSend}<span>· Ctrl/⌘ + Enter</span>{/if}</small>{#if onSend}<button class="md-send" type="button" disabled={!value.trim() || disabled || busy} onclick={() => onSend?.()}><Send size={13} />{busy ? (de ? "Wird gesendet …" : "Sending …") : (de ? "Kommentar senden" : "Post comment")}</button>{/if}</div>
</div>
<style>
.md-editor{width:100%;min-width:0;color:var(--color-ink);font:400 12px/1.5 var(--font-sans)}