diff --git a/.changeset/long-parrots-give.md b/.changeset/long-parrots-give.md new file mode 100644 index 00000000000..269c5130bba --- /dev/null +++ b/.changeset/long-parrots-give.md @@ -0,0 +1,5 @@ +--- +'@astrojs/starlight': patch +--- + +Improve inline code and code block support in RTL languages diff --git a/packages/starlight/__tests__/remark-rehype/code-rtl-support.test.ts b/packages/starlight/__tests__/remark-rehype/code-rtl-support.test.ts new file mode 100644 index 00000000000..69aa85ad8e2 --- /dev/null +++ b/packages/starlight/__tests__/remark-rehype/code-rtl-support.test.ts @@ -0,0 +1,25 @@ +import { rehype } from 'rehype'; +import { expect, test } from 'vitest'; +import { rehypeRtlCodeSupport } from '../../integrations/code-rtl-support'; + +const processor = rehype().data('settings', { fragment: true }).use(rehypeRtlCodeSupport()); + +test('applies `dir="auto"` to inline code', async () => { + const input = `
Some text with inline code
.
Some text with inline code
.
Some text in a paragraph:
console.log('test')
`;
+ const output = String(await processor.process(input));
+ expect(output).not.toEqual(input);
+ expect(output).includes('dir="ltr"');
+ expect(output).toMatchInlineSnapshot(
+ '"Some text in a paragraph:
console.log(\'test\')
"'
+ );
+});
diff --git a/packages/starlight/__tests__/remark-rehype/vitest.config.ts b/packages/starlight/__tests__/remark-rehype/vitest.config.ts
new file mode 100644
index 00000000000..0b12dc3a687
--- /dev/null
+++ b/packages/starlight/__tests__/remark-rehype/vitest.config.ts
@@ -0,0 +1,3 @@
+import { defineVitestConfig } from '../test-config';
+
+export default defineVitestConfig({ title: 'Remark-Rehype' });
diff --git a/packages/starlight/index.ts b/packages/starlight/index.ts
index dbd487bdded..5909777eb92 100644
--- a/packages/starlight/index.ts
+++ b/packages/starlight/index.ts
@@ -8,6 +8,7 @@ import { starlightSitemap } from './integrations/sitemap';
import { vitePluginStarlightUserConfig } from './integrations/virtual-user-config';
import { errorMap } from './utils/error-map';
import { StarlightConfigSchema, StarlightUserConfig } from './utils/user-config';
+import { rehypeRtlCodeSupport } from './integrations/code-rtl-support';
export default function StarlightIntegration(opts: StarlightUserConfig): AstroIntegration[] {
const parsedConfig = StarlightConfigSchema.safeParse(opts, { errorMap });
@@ -39,6 +40,7 @@ export default function StarlightIntegration(opts: StarlightUserConfig): AstroIn
},
markdown: {
remarkPlugins: [...starlightAsides()],
+ rehypePlugins: [rehypeRtlCodeSupport()],
shikiConfig:
// Configure Shiki theme if the user is using the default github-dark theme.
config.markdown.shikiConfig.theme !== 'github-dark' ? {} : { theme: 'css-variables' },
diff --git a/packages/starlight/integrations/code-rtl-support.ts b/packages/starlight/integrations/code-rtl-support.ts
new file mode 100644
index 00000000000..b8f7c3a6660
--- /dev/null
+++ b/packages/starlight/integrations/code-rtl-support.ts
@@ -0,0 +1,31 @@
+import type { Root } from 'hastscript/lib/core';
+import { CONTINUE, SKIP, visit } from 'unist-util-visit';
+
+/**
+ * rehype plugin that adds `dir` attributes to `` and ``
+ * elements that don’t already have them.
+ *
+ * `` will become ``
+ * `` will become ``
+ *
+ * `` _inside_ `` is skipped, so respects the `ltr` on its parent.
+ *
+ * Reasoning:
+ * - `` is usually a code block and code should be LTR even in an RTL document
+ * - `` is often LTR, but could also be RTL. `dir="auto"` ensures the bidirectional
+ * algorithm treats the contents of `` in isolation and gives its best guess.
+ */
+export function rehypeRtlCodeSupport() {
+ return () => (root: Root) => {
+ visit(root, 'element', (el) => {
+ if (el.tagName === 'pre' || el.tagName === 'code') {
+ el.properties ||= {};
+ if (!('dir' in el.properties)) {
+ el.properties.dir = { pre: 'ltr', code: 'auto' }[el.tagName];
+ }
+ return SKIP;
+ }
+ return CONTINUE;
+ });
+ };
+}