diff --git a/src/client/src/components/CodeViewer.ts b/src/client/src/components/CodeViewer.ts index 7523f15..91b132a 100644 --- a/src/client/src/components/CodeViewer.ts +++ b/src/client/src/components/CodeViewer.ts @@ -55,6 +55,7 @@ export class CodeViewer extends LitElement { EditorView.editable.of(false), EditorView.lineWrapping, viewerTheme, + ...bidiTextExtensions(this.language), ...languageExtensions(this.language), ], }), @@ -97,6 +98,19 @@ const viewerTheme = EditorView.theme({ }, }); +const bidiTextTheme = EditorView.theme({ + ".cm-content": { + textAlign: "start", + }, + ".cm-line": { + unicodeBidi: "plaintext", + }, +}); + +function bidiTextExtensions(language: string | undefined): Extension[] { + return language === "markdown" ? [EditorView.contentAttributes.of({ dir: "auto" }), bidiTextTheme] : []; +} + function languageExtensions(language: string | undefined): Extension[] { if (language === undefined) return []; switch (language) { diff --git a/src/client/src/components/FormattedText.ts b/src/client/src/components/FormattedText.ts index 259204b..dccf74e 100644 --- a/src/client/src/components/FormattedText.ts +++ b/src/client/src/components/FormattedText.ts @@ -9,7 +9,7 @@ export class FormattedText extends LitElement { @property() text = ""; override render() { - return html`
${unsafeHTML(toSafeMarkdownHtml(this.text))}
`; + return html`
${unsafeHTML(toSafeMarkdownHtml(this.text))}
`; } override updated(): void { diff --git a/src/client/src/components/PromptEditor.ts b/src/client/src/components/PromptEditor.ts index 449c045..ee88588 100644 --- a/src/client/src/components/PromptEditor.ts +++ b/src/client/src/components/PromptEditor.ts @@ -433,6 +433,7 @@ const proseInputAssistanceAttributes: Record = { autocorrect: "on", autocapitalize: "sentences", writingsuggestions: "true", + dir: "auto", }; const codeLikeInputAssistanceAttributes: Record = { @@ -440,6 +441,7 @@ const codeLikeInputAssistanceAttributes: Record = { autocorrect: "off", autocapitalize: "off", writingsuggestions: "false", + dir: "auto", }; function inputAssistanceContentAttributes(draftBeforeCursor: string): Record { diff --git a/src/client/src/components/SessionList.ts b/src/client/src/components/SessionList.ts index e2c156d..fea428c 100644 --- a/src/client/src/components/SessionList.ts +++ b/src/client/src/components/SessionList.ts @@ -134,7 +134,7 @@ export class SessionList extends LitElement implements KeyboardNavigableSection const selectedTitle = this.selected?.path ?? selectedSummary; return html`

- + ${this.renderCurrentSelectionButton(currentSessions)} ${sessionCount} @@ -213,7 +213,7 @@ export class SessionList extends LitElement implements KeyboardNavigableSection >
${showsCheckbox ? html` { event.stopPropagation(); }} @change=${() => { this.toggleSelected(session.id); }}>` : null} - ${row.depth > 0 ? html`` : null}${sessionLabel(session)}${row.depth > 2 ? html` depth ${row.depth}` : null}${row.hasMissingParent ? html` parent unavailable` : null}${this.renderSessionMetaPrefix(session)}${String(session.messageCount)} messages + ${row.depth > 0 ? html`` : null}${sessionLabel(session)}${row.depth > 2 ? html` depth ${row.depth}` : null}${row.hasMissingParent ? html` parent unavailable` : null}${this.renderSessionMetaPrefix(session)}${String(session.messageCount)} messages ${this.renderActivity(session)}
@@ -375,6 +375,7 @@ export class SessionList extends LitElement implements KeyboardNavigableSection .bulk-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 0 0 6px; } .bulk-row button { padding: 5px 7px; font-size: 12px; } .bulk-row small { display: inline; min-width: 0; color: var(--pi-muted); } + .action-name, .section-selected { text-align: start; unicode-bidi: plaintext; } .bulk-row .capability-hint { flex: 1 0 100%; color: var(--pi-warning); } .bulk-row.selecting { padding: 6px; border: 1px solid var(--pi-border-muted); border-radius: 8px; background: color-mix(in srgb, var(--pi-surface) 65%, transparent); } button.danger, .action-menu-panel button.danger { color: var(--pi-danger); } diff --git a/src/client/src/components/shared.ts b/src/client/src/components/shared.ts index 91165e5..f7f24dd 100644 --- a/src/client/src/components/shared.ts +++ b/src/client/src/components/shared.ts @@ -345,6 +345,7 @@ export const chatStyles = css` .msg-meta:focus::before, .msg-meta.expanded::before { content: ""; } } formatted-text.part { display: block; } + formatted-text.part, .queued-message formatted-text { text-align: start; unicode-bidi: plaintext; } .part { max-width: 100%; min-width: 0; box-sizing: border-box; overflow: visible; } .part + .part { margin-top: 10px; } .tool-line { color: var(--pi-warning); } @@ -355,22 +356,22 @@ export const chatStyles = css` .skill-invocation > summary, .skill-read > strong { color: var(--pi-purple); } .skill-invocation > small, .skill-read > small { display: block; margin: 6px 0 0; color: var(--pi-muted); } summary { cursor: pointer; color: var(--pi-muted); } - pre { margin: 6px 0 0; white-space: pre-wrap; overflow-wrap: anywhere; font: inherit; } - .shell-output { color: var(--pi-text); font: 13px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; line-height: 1.45; } + pre { margin: 6px 0 0; white-space: pre-wrap; overflow-wrap: anywhere; font: inherit; direction: ltr; text-align: left; unicode-bidi: isolate; } + .shell-output { color: var(--pi-text); font: 13px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; line-height: 1.45; direction: ltr; text-align: left; unicode-bidi: isolate; } @keyframes pulse { 0%, 100% { transform: scale(.75); opacity: .55; } 50% { transform: scale(1.2); opacity: 1; } } `; export const formattedTextStyles = css` :host { display: block; } - .formatted { white-space: normal; overflow-wrap: anywhere; line-height: 1.45; } + .formatted { white-space: normal; overflow-wrap: anywhere; line-height: 1.45; text-align: start; unicode-bidi: plaintext; } p, ul, ol, pre, blockquote, table, .code-block-wrapper { margin: 0 0 10px; } :is(p, ul, ol, pre, blockquote, table, .code-block-wrapper):last-child { margin-bottom: 0; } ul, ol { padding-left: 22px; } li + li { margin-top: 3px; } - code { border: 1px solid var(--pi-border); border-radius: 4px; background: var(--pi-bg); padding: 1px 4px; font: 13px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; } + code { border: 1px solid var(--pi-border); border-radius: 4px; background: var(--pi-bg); padding: 1px 4px; font: 13px ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; direction: ltr; text-align: left; unicode-bidi: isolate; } .code-block-wrapper { position: relative; } .code-block-wrapper pre { margin: 0; padding-right: 40px; } - pre { border: 1px solid var(--pi-border); border-radius: 8px; background: var(--pi-bg); padding: 10px; overflow-x: auto; overflow-y: hidden; } + pre { border: 1px solid var(--pi-border); border-radius: 8px; background: var(--pi-bg); padding: 10px; overflow-x: auto; overflow-y: hidden; direction: ltr; text-align: left; unicode-bidi: isolate; } pre code { border: 0; padding: 0; background: transparent; } .code-copy-button { position: absolute; top: 6px; right: 6px; z-index: 1; display: inline-grid; place-items: center; width: 24px; height: 24px; border: 1px solid var(--pi-border); border-radius: 6px; background: var(--pi-surface); color: var(--pi-muted); padding: 0; font: 14px system-ui, sans-serif; line-height: 1; cursor: pointer; } .code-copy-button:hover, .code-copy-button:focus { color: var(--pi-text); border-color: var(--pi-accent); } @@ -466,8 +467,8 @@ export const promptEditorStyles = css` textarea, .markdown-editor .cm-editor { box-sizing: border-box; width: 100%; min-height: 54px; max-height: 220px; resize: none; overflow: hidden; border-radius: 8px; border: 1px solid var(--pi-border); background: var(--pi-bg); color: var(--pi-text); font: 16px/1.4 system-ui, sans-serif; } textarea { overflow-y: auto; padding: 8px; } .markdown-editor .cm-scroller { max-height: 220px; overflow-y: auto; font-family: system-ui, sans-serif; line-height: 1.4; } - .markdown-editor .cm-content { min-height: 38px; padding: 8px 44px 8px 8px; caret-color: var(--pi-text); } - .markdown-editor .cm-line { padding: 0; } + .markdown-editor .cm-content { min-height: 38px; padding: 8px 44px 8px 8px; caret-color: var(--pi-text); text-align: start; unicode-bidi: plaintext; } + .markdown-editor .cm-line { padding: 0; unicode-bidi: plaintext; } .markdown-editor .cm-placeholder { color: var(--pi-dim); } .markdown-editor .cm-focused { outline: none; } .shell-mode textarea, .shell-mode .markdown-editor .cm-editor { border-color: var(--pi-success); box-shadow: 0 0 0 1px var(--pi-success-ring); }