From b48e34600116607a8f731c2003cd9dcc844490ed Mon Sep 17 00:00:00 2001 From: Alois Date: Sun, 9 Aug 2026 23:22:24 +0200 Subject: [PATCH 01/13] feat(code-block): last-minute styling --- src/markdown/code-block.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/markdown/code-block.tsx b/src/markdown/code-block.tsx index 156a2c7..3266dbc 100644 --- a/src/markdown/code-block.tsx +++ b/src/markdown/code-block.tsx @@ -71,7 +71,7 @@ function CodeBlock({ data-slot="code-block" data-line-numbers={showLineNumbers || undefined} data-language={language || undefined} - className="group/code-block overflow-hidden rounded-lg border-[length:var(--ui-border-width)] border-border bg-[var(--markdown-code-background)] group-data-[variant=compact]/markdown:my-[0.45rem]" + className="group/code-block overflow-hidden rounded-lg border-[length:var(--ui-border-width)] border-border bg-[var(--markdown-code-background)] my-2!" >
Date: Sun, 9 Aug 2026 23:41:51 +0200 Subject: [PATCH 02/13] feat(markdown): add ??++ --- README.md | 34 ---------------------- package.json | 2 +- src/markdown/markdown.tsx | 2 ++ src/markdown/remark-small-muted.ts | 45 ++++++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 35 deletions(-) create mode 100644 src/markdown/remark-small-muted.ts diff --git a/README.md b/README.md index a67747e..70a4a7b 100644 --- a/README.md +++ b/README.md @@ -3,37 +3,3 @@ ```sh pnpm install https://git.methanium.net/methanium/ui/releases/download/latest/methanium-ui.tgz ``` - -## Markdown - -The renderer is available from the package root. The live editor, compact text -renderer, and emoji APIs use the `@methanium/ui/markdown` entry point. - -```tsx -import { Markdown } from "@methanium/ui"; -import { - EmojiProvider, - Input, - createEmojiRegistry, -} from "@methanium/ui/markdown"; - -const registry = createEmojiRegistry({ - customEmojis: [ - { - shortcode: ":party_parrot:", - aliases: [":parrot_party:"], - src: "/emoji/party-parrot.webp", - }, - ], -}); - - - - -; -``` - -Custom shortcodes and aliases must be unique and may not collide with built-in -Twemoji names. Twemoji graphics are bundled locally and do not use a CDN. The -graphics are provided by the [Twemoji project](https://github.com/jdecked/twemoji) -under CC-BY 4.0; code is licensed under MIT. diff --git a/package.json b/package.json index 60b620c..adaec3d 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@methanium/ui", - "version": "0.0.25", + "version": "0.0.26", "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/src/markdown/markdown.tsx b/src/markdown/markdown.tsx index 07caf8c..99a3338 100644 --- a/src/markdown/markdown.tsx +++ b/src/markdown/markdown.tsx @@ -38,6 +38,7 @@ import { EmojiProvider, useEmojiRegistry } from "./emoji-context"; import type { EmojiRegistry } from "./emoji-data"; import { parseCodeMeta, remarkCodeMeta } from "./markdown-meta"; import { remarkEmoji } from "./remark-emoji"; +import { remarkSmallMuted } from "./remark-small-muted"; export type MarkdownTheme = "light" | "dark"; export type MarkdownVariant = "article" | "compact"; @@ -147,6 +148,7 @@ function Markdown({ remarkMath, remarkBreaks, remarkCodeMeta, + remarkSmallMuted, [remarkEmoji, { registry }], ]; diff --git a/src/markdown/remark-small-muted.ts b/src/markdown/remark-small-muted.ts new file mode 100644 index 0000000..6beadd1 --- /dev/null +++ b/src/markdown/remark-small-muted.ts @@ -0,0 +1,45 @@ +import type { Emphasis, Parent, Root, Text } from "mdast"; +import { SKIP, visit } from "unist-util-visit"; + +const delimiter = "??++"; + +export function remarkSmallMuted() { + return (tree: Root) => { + visit(tree, "text", (node: Text, index, parent: Parent | undefined) => { + if (index === undefined || !parent) return; + + const children: Parent["children"] = []; + let cursor = 0; + let opening = node.value.indexOf(delimiter); + + while (opening !== -1) { + const contentStart = opening + delimiter.length; + const closing = node.value.indexOf(delimiter, contentStart); + if (closing === -1) break; + + if (opening > cursor) { + children.push({ type: "text", value: node.value.slice(cursor, opening) }); + } + children.push({ + type: "emphasis", + children: [{ type: "text", value: node.value.slice(contentStart, closing) }], + data: { + hName: "span", + hProperties: { className: ["text-xs", "text-muted-foreground"] }, + }, + } satisfies Emphasis); + + cursor = closing + delimiter.length; + opening = node.value.indexOf(delimiter, cursor); + } + + if (children.length === 0) return; + if (cursor < node.value.length) { + children.push({ type: "text", value: node.value.slice(cursor) }); + } + + parent.children.splice(index, 1, ...children); + return [SKIP, index + children.length]; + }); + }; +} From 5e2120906e3b45e62f3dc06497c9d9b91c4b5c5c Mon Sep 17 00:00:00 2001 From: Alois Date: Sun, 9 Aug 2026 23:56:58 +0200 Subject: [PATCH 03/13] feat(markdown): add code fencing for ??++ --- apps/showcase/src/MarkdownShowcase.tsx | 2 +- package.json | 2 +- src/markdown/markdown.tsx | 41 +++++++++++++++++++++++++- 3 files changed, 42 insertions(+), 3 deletions(-) diff --git a/apps/showcase/src/MarkdownShowcase.tsx b/apps/showcase/src/MarkdownShowcase.tsx index ded9d7a..ddf95a1 100644 --- a/apps/showcase/src/MarkdownShowcase.tsx +++ b/apps/showcase/src/MarkdownShowcase.tsx @@ -28,7 +28,7 @@ const markdownContent = [ "", "Shortcodes use local Twemoji assets :fire: :white_check_mark:, while applications can register their own images :methanium:.", "", - "Visit the [Methanium repository](https://github.com/methanium) or jump to the [code examples](#code-examples). Bare URLs are supported too: https://example.com.", + "Visit the [Methanium](https://methanium.net) or jump to the [code examples](#code-examples). Bare URLs are supported too: https://example.com.", "", "![Methanium logo](" + methaniumLogo + ")", "", diff --git a/package.json b/package.json index adaec3d..1682bf8 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@methanium/ui", - "version": "0.0.26", + "version": "0.0.27", "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/src/markdown/markdown.tsx b/src/markdown/markdown.tsx index 99a3338..1176c38 100644 --- a/src/markdown/markdown.tsx +++ b/src/markdown/markdown.tsx @@ -81,6 +81,45 @@ function classNames(node: Element) { return value ? [String(value)] : []; } +function moveFenceNotes(content: string) { + const lines = content.split("\n"); + const fence = /^ {0,3}`{3,}/; + const closingFence = /^ {0,3}`{3,}\s*$/; + const note = /\?\?\+\+.*?\?\?\+\+/g; + let inCodeBlock = false; + let openingIndex = -1; + let openingNotes: string[] = []; + + for (let index = 0; index < lines.length; index += 1) { + const line = lines[index]; + if (inCodeBlock) { + const closingNotes = line.match(note) ?? []; + const cleanedLine = line.replace(note, "").trimEnd(); + if (closingFence.test(cleanedLine)) { + if (openingNotes.length > 0) { + lines[openingIndex] = lines[openingIndex].replace(note, "").trimEnd(); + } + lines[index] = cleanedLine; + inCodeBlock = false; + const notes = [...openingNotes, ...closingNotes]; + if (notes.length > 0) { + lines.splice(index + 1, 0, ...notes); + index += notes.length; + } + openingNotes = []; + } + continue; + } + if (!fence.test(line)) continue; + + inCodeBlock = true; + openingIndex = index; + openingNotes = line.match(note) ?? []; + } + + return lines.join("\n"); +} + const defaultComponents: Components = { a: MarkdownLink, blockquote: MarkdownBlockquote, @@ -171,7 +210,7 @@ function Markdown({ rehypePlugins={rehypePlugins} components={{ ...defaultComponents, ...components }} > - {content} + {moveFenceNotes(content)} From 5b52e1ae29e5d4bf57541402f225025c045b82b4 Mon Sep 17 00:00:00 2001 From: Rasensprenger Date: Mon, 10 Aug 2026 00:02:40 +0200 Subject: [PATCH 04/13] chore(deps): lock file maintenance --- pnpm-lock.yaml | 318 +++---------------------------------------------- 1 file changed, 17 insertions(+), 301 deletions(-) diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index a500ae3..6fab24d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -10,7 +10,7 @@ importers: dependencies: '@base-ui/react': specifier: ^1.7.0 - version: 1.7.0(@date-fns/tz@1.5.0)(@types/react@19.2.18)(date-fns@4.4.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + version: 1.7.0(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) '@codemirror/autocomplete': specifier: ^6.20.3 version: 6.20.3 @@ -79,7 +79,7 @@ importers: version: 4.12.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8) recharts: specifier: 3.10.1 - version: 3.10.1(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react-is@19.2.7)(react@19.2.8)(redux@5.0.1) + version: 3.10.1(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react-is@19.2.8)(react@19.2.8)(redux@5.0.1) rehype-autolink-headings: specifier: ^7.1.0 version: 7.1.0 @@ -146,7 +146,7 @@ importers: version: 7.0.2 vite: specifier: ^8.2.1 - version: 8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0) + version: 8.2.1(@types/node@26.2.0)(jiti@2.7.0) apps/showcase: dependencies: @@ -165,7 +165,7 @@ importers: version: 10.0.1(eslint@10.8.1(jiti@2.7.0)) '@tailwindcss/vite': specifier: ^4.3.3 - version: 4.3.3(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)) + version: 4.3.3(vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0)) '@types/node': specifier: ^26.2.0 version: 26.2.0 @@ -180,7 +180,7 @@ importers: version: typescript@7.0.2 '@vitejs/plugin-react': specifier: ^6.0.5 - version: 6.0.5(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0)) + version: 6.0.5(vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0)) eslint: specifier: ^10.8.1 version: 10.8.1(jiti@2.7.0) @@ -201,7 +201,7 @@ importers: version: 8.66.0(@typescript/typescript6@6.0.2)(eslint@10.8.1(jiti@2.7.0)) vite: specifier: ^8.2.1 - version: 8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0) + version: 8.2.1(@types/node@26.2.0)(jiti@2.7.0) packages: @@ -395,9 +395,6 @@ packages: '@codemirror/view@6.43.8': resolution: {integrity: sha512-qtItTDssZ/5GFfi94hrILu9j/VUeFPDPkhovEfmWFj2ipTxnzPB8DdHgfbb8HYTzLTYhrndKmyQxXUz/PDLenw==} - '@date-fns/tz@1.5.0': - resolution: {integrity: sha512-lwYN/vDPeNRULcepoE/LO2Pgx+7/RV+S9ARfbc9lr2DtGkOD7pAiruHvbR1RX3Qyf6ja47EWJDMsNK5vK08DJg==} - '@dotenvx/dotenvx@1.75.1': resolution: {integrity: sha512-/BITOC9dmS/edY2zQwZNicQ059O6RKabtQfyEafV0nGtfYRNHYy1DIPiYVcov40+tob9hfmBnbR963dS+EQ1DQ==} hasBin: true @@ -405,162 +402,6 @@ packages: '@dotenvx/primitives@0.8.0': resolution: {integrity: sha512-VYJy0uhFm9zTJ1TxBaW/pA8bjbOM/OttaNMwZ1RHG4JKyRG7DhSdiqD1ipQoAyoD22olUtxbP78W9xY3Wd11bg==} - '@esbuild/aix-ppc64@0.28.1': - resolution: {integrity: sha512-Svl7tq8k/08+p6CXPpRjQ1fKX+1odH/BQbb48fV6fj3CWHhsoIOoY87w1oHXm0qEpkIK3ZfVgp0hed3XBXzXMQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [aix] - - '@esbuild/android-arm64@0.28.1': - resolution: {integrity: sha512-34EGEbCIAgosYz6goLcopX6Mo7NyGv9tfwEM2/7Ce2VcVRk568iSvniGWcUXIy7wEDR1wzolcxcriFVrWYcwBg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [android] - - '@esbuild/android-arm@0.28.1': - resolution: {integrity: sha512-0k2F129Xdio1TdJfzJ8sy1Q47vUD2NnwdhiAf7drUN1EBTfPf4hsFCtmMgu/6m8JSzsBrlmVjudMBQqOfG8usQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [android] - - '@esbuild/android-x64@0.28.1': - resolution: {integrity: sha512-dbwY7ltSMDWsRatcRpCnES4F+im88OCUgGZjy52shC7GqHRE/cYlxNbB4Z4UpJswpcc4Qxd2oE/ufM0p61IKng==} - engines: {node: '>=18'} - cpu: [x64] - os: [android] - - '@esbuild/darwin-arm64@0.28.1': - resolution: {integrity: sha512-TZbWkQY7kvTAXbXUT7uVACR5cMHsDiSz9z7ZKAX/RTq/WJEk3QyRr0wZpNhBDX+/0CtdqUIJlOiodQcta6tY3Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [darwin] - - '@esbuild/darwin-x64@0.28.1': - resolution: {integrity: sha512-zfdzgK9ACBNZLI/CyHTOx81SyNbM6YXn7rxSgX97VjyiPl9W1i4Ka4fgKECEoFCKGpvBj5qArWIGgQjOwkgskQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [darwin] - - '@esbuild/freebsd-arm64@0.28.1': - resolution: {integrity: sha512-wG2EA8ENdEI0qhkSZMjfqrdY+ziCYCPMmtZjjIwOmXFjmyzEHn+UUxk5of+SYsjtfs3VpnlC7QLzSI5hY/rOAw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [freebsd] - - '@esbuild/freebsd-x64@0.28.1': - resolution: {integrity: sha512-i7dZ9vQgnvSCzi/rYCXNgtF/U+eKZNJBzu3eTQbRgHnM7tNSizLOkRFAl3qzVc/Op/u5YkHHa4pf/3DOYHthLQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [freebsd] - - '@esbuild/linux-arm64@0.28.1': - resolution: {integrity: sha512-yHs+0uc8+nvEAfAfxrWQKK5peSNzBc4PegcMO0EJ2hT71uA7vB8Ihg2e77R2P7SG5uYjPbHlLLmve4LLLRCf0g==} - engines: {node: '>=18'} - cpu: [arm64] - os: [linux] - - '@esbuild/linux-arm@0.28.1': - resolution: {integrity: sha512-qVXBOHQS+d5Y722GwJzJUtOLlX7km3CraOaGormF1pDtPd2C/l1SHRPgjLunLGe51Sh5YYWKMFDyV4SxgMQYTQ==} - engines: {node: '>=18'} - cpu: [arm] - os: [linux] - - '@esbuild/linux-ia32@0.28.1': - resolution: {integrity: sha512-d1z4ZuP0ajrfz/FhGT4vv278rX8KnPPJx8i5+AtK7TYbx9Le9F1hyzurZpkEyjkGa9dUGhQow4C1NmeGvqxN2w==} - engines: {node: '>=18'} - cpu: [ia32] - os: [linux] - - '@esbuild/linux-loong64@0.28.1': - resolution: {integrity: sha512-M5sRjUVZrkm1OAPR3dlOYzNmN+loZKGVi1VUQGrwuqLcbR6qeAz+famMhjASeH3YVKvZz+zT1jlh/keC3Rj/lg==} - engines: {node: '>=18'} - cpu: [loong64] - os: [linux] - - '@esbuild/linux-mips64el@0.28.1': - resolution: {integrity: sha512-mRObBZeHh2OxcBFPWE/FjylkRgZdYuiTR3vaTozquCGOH14iP9oN4x4Ge81CoIDYQrXmIxpFumJBu5MtZpnQJQ==} - engines: {node: '>=18'} - cpu: [mips64el] - os: [linux] - - '@esbuild/linux-ppc64@0.28.1': - resolution: {integrity: sha512-slScBsMAb3GFDcdrCgLwZtPYRoH2H/youv10QiZyRjmsP48fznoveWytSgCI/R0ZcUgpc0ZhIUEx6LHts8yrfQ==} - engines: {node: '>=18'} - cpu: [ppc64] - os: [linux] - - '@esbuild/linux-riscv64@0.28.1': - resolution: {integrity: sha512-kw0owk1o0GFETUJyW0jc0G4Yzs0BHZn0JDZ8JRT088vjJYX777BAs1fDGxAC+q831qOs2DTC96mNsG2opdfyyQ==} - engines: {node: '>=18'} - cpu: [riscv64] - os: [linux] - - '@esbuild/linux-s390x@0.28.1': - resolution: {integrity: sha512-/lAIjX8aYFRByhh6L5rYtPEDRqa9de/4V/juOXcta5frjvzXO4/sqEtyytse0g3zZFuWu5cDN0MkLz2qRDD2Ag==} - engines: {node: '>=18'} - cpu: [s390x] - os: [linux] - - '@esbuild/linux-x64@0.28.1': - resolution: {integrity: sha512-u/anNYF2mmVOEDwLtnQ1wOr3EZ9sTNGLWrsYGYwHWzGA3Si84IOkHXlbWTD1NB+9/1lcnweYKO54uhxZydNzfA==} - engines: {node: '>=18'} - cpu: [x64] - os: [linux] - - '@esbuild/netbsd-arm64@0.28.1': - resolution: {integrity: sha512-oks0DYbLwWMmaakTsCb+zL4E+aHRVLom9IJZOAthMQEPiQmydXHkziYEsGYRx0uNV/IjEKGAV941JzH02pflqw==} - engines: {node: '>=18'} - cpu: [arm64] - os: [netbsd] - - '@esbuild/netbsd-x64@0.28.1': - resolution: {integrity: sha512-aeL6lAnN89Hz43Mlh1G8ARasbuoYvSITDEx0tHh5b7jJnHcssqgjy9Yx430GDpmCa6OyrKoS0aNRjKundRizGg==} - engines: {node: '>=18'} - cpu: [x64] - os: [netbsd] - - '@esbuild/openbsd-arm64@0.28.1': - resolution: {integrity: sha512-MEFJe5C3R8pwXdZ5Y21oo6m7ePiS0d9pWucn99O/wvyJZChoIQKrQDxKrGeW8F5+T0okTHesAmDeiHDTIq0V/Q==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openbsd] - - '@esbuild/openbsd-x64@0.28.1': - resolution: {integrity: sha512-i/ZLIOafE0Z8cI/XANJAixoJL/uRAoS2xOA3rb0xN+KK0K177cMAsQYkzHtBrtMXAKuAc7HGgcWiZ/sRC1Nxgw==} - engines: {node: '>=18'} - cpu: [x64] - os: [openbsd] - - '@esbuild/openharmony-arm64@0.28.1': - resolution: {integrity: sha512-ge+Z7EXFNt2BO1oAMsVpiQ8EwndV9i1xXerAeTIK7AtPs3bKFXQM7nlRxDSIUIMeueR1CNXxqztLzdNeReKBJg==} - engines: {node: '>=18'} - cpu: [arm64] - os: [openharmony] - - '@esbuild/sunos-x64@0.28.1': - resolution: {integrity: sha512-BEjgtECkL3vY+SaSQ6nzVfiALUeFxpawyp8Jmf5PtYhf1Ug40N1h/hxlhts+f1FvSvarEigdxS3BlSMI2PJLcQ==} - engines: {node: '>=18'} - cpu: [x64] - os: [sunos] - - '@esbuild/win32-arm64@0.28.1': - resolution: {integrity: sha512-lCv9eK/H6ZJWbE7bh2nw54CZ9M2nupBxJcTsdk/QQnWkdSjKGuxmmH8/GWrlT1eMmZfn4dGcCjRte397WqfQXA==} - engines: {node: '>=18'} - cpu: [arm64] - os: [win32] - - '@esbuild/win32-ia32@0.28.1': - resolution: {integrity: sha512-zvb/mB2bSCoJOpoCBgYKKpX6YM6mJBlBUVUtVj41DlZJVEB6/0CKlRYxP5wWl1C1ILiCoAU5wZZ4q1P3qeS6Eg==} - engines: {node: '>=18'} - cpu: [ia32] - os: [win32] - - '@esbuild/win32-x64@0.28.1': - resolution: {integrity: sha512-bm4Mowrv+GXMlpWX++EcXw/iLyd1o3+bJkC2DkWXYVvgZCqD/bSj9ctZeAMC3cIxgjRVR2Dufaiu4YPxr5gW1A==} - engines: {node: '>=18'} - cpu: [x64] - os: [win32] - '@eslint-community/eslint-utils@4.10.1': resolution: {integrity: sha512-cuadcxVFE8sDK6iWJbs8Sn0av2Nrh2QSGQhVlBW9AaAHqHwjWsZHT8LJ4hFGPh7ASBV2deFdM7H/DPjulmh8rg==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -1756,9 +1597,6 @@ packages: resolution: {integrity: sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==} engines: {node: '>=12'} - date-fns@4.4.0: - resolution: {integrity: sha512-+1UMbeh68lH1SegH83CGWwpb6OHHbpSgr3+s5Eww5M4CAgswBpoWS0AjTOfEJ33HiYKz1hdj/KTFprzXHmq/6w==} - debounce-fn@4.0.0: resolution: {integrity: sha512-8pYCQiL9Xdcg0UPSD3d+0KMlOjp+KGU5EPwYddgzQ7DATsg4fuUDjQtsYLmWjnk2obnNHgV3vE2Y4jejSOJVBQ==} engines: {node: '>=10'} @@ -1917,11 +1755,6 @@ packages: es-toolkit@1.50.0: resolution: {integrity: sha512-OyZKhUVvEep9ITEiwHn8GKnMRQIVqoSIX7WnRbkWgJkllCujilqP2rD0u979tkl8wqyc8ICwlc1UBVv/Sl1G6w==} - esbuild@0.28.1: - resolution: {integrity: sha512-HrJrvZv5ayxBzPfwphOoNzkzOIIlifzk0KJrGK2c8R4+LKpMtpYLQeUdjnwjWv/LZlkH2laZk+4w78pi99D4Vw==} - engines: {node: '>=18'} - hasBin: true - escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} @@ -3058,8 +2891,8 @@ packages: peerDependencies: react: ^19.2.8 - react-is@19.2.7: - resolution: {integrity: sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==} + react-is@19.2.8: + resolution: {integrity: sha512-s5un28nYxKJw5gvUHyW5PCC28CvBqLu9r3cWgzHT4Vo/5fqqkFcdRYsGcKf50WMPpjjFZS5d76fn3YCo2njKwQ==} react-markdown@10.1.0: resolution: {integrity: sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==} @@ -3826,7 +3659,7 @@ snapshots: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 - '@base-ui/react@1.7.0(@date-fns/tz@1.5.0)(@types/react@19.2.18)(date-fns@4.4.0)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': + '@base-ui/react@1.7.0(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: '@babel/runtime': 7.29.7 '@base-ui/utils': 0.3.2(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -3836,9 +3669,7 @@ snapshots: react-dom: 19.2.8(react@19.2.8) use-sync-external-store: 1.6.0(react@19.2.8) optionalDependencies: - '@date-fns/tz': 1.5.0 '@types/react': 19.2.18 - date-fns: 4.4.0 '@base-ui/utils@0.3.2(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8)': dependencies: @@ -3931,9 +3762,6 @@ snapshots: style-mod: 4.1.3 w3c-keyname: 2.2.8 - '@date-fns/tz@1.5.0': - optional: true - '@dotenvx/dotenvx@1.75.1': dependencies: '@dotenvx/primitives': 0.8.0 @@ -3955,84 +3783,6 @@ snapshots: '@dotenvx/primitives@0.8.0': {} - '@esbuild/aix-ppc64@0.28.1': - optional: true - - '@esbuild/android-arm64@0.28.1': - optional: true - - '@esbuild/android-arm@0.28.1': - optional: true - - '@esbuild/android-x64@0.28.1': - optional: true - - '@esbuild/darwin-arm64@0.28.1': - optional: true - - '@esbuild/darwin-x64@0.28.1': - optional: true - - '@esbuild/freebsd-arm64@0.28.1': - optional: true - - '@esbuild/freebsd-x64@0.28.1': - optional: true - - '@esbuild/linux-arm64@0.28.1': - optional: true - - '@esbuild/linux-arm@0.28.1': - optional: true - - '@esbuild/linux-ia32@0.28.1': - optional: true - - '@esbuild/linux-loong64@0.28.1': - optional: true - - '@esbuild/linux-mips64el@0.28.1': - optional: true - - '@esbuild/linux-ppc64@0.28.1': - optional: true - - '@esbuild/linux-riscv64@0.28.1': - optional: true - - '@esbuild/linux-s390x@0.28.1': - optional: true - - '@esbuild/linux-x64@0.28.1': - optional: true - - '@esbuild/netbsd-arm64@0.28.1': - optional: true - - '@esbuild/netbsd-x64@0.28.1': - optional: true - - '@esbuild/openbsd-arm64@0.28.1': - optional: true - - '@esbuild/openbsd-x64@0.28.1': - optional: true - - '@esbuild/openharmony-arm64@0.28.1': - optional: true - - '@esbuild/sunos-x64@0.28.1': - optional: true - - '@esbuild/win32-arm64@0.28.1': - optional: true - - '@esbuild/win32-ia32@0.28.1': - optional: true - - '@esbuild/win32-x64@0.28.1': - optional: true - '@eslint-community/eslint-utils@4.10.1(eslint@10.8.1(jiti@2.7.0))': dependencies: eslint: 10.8.1(jiti@2.7.0) @@ -4566,12 +4316,12 @@ snapshots: '@tailwindcss/oxide-win32-arm64-msvc': 4.3.3 '@tailwindcss/oxide-win32-x64-msvc': 4.3.3 - '@tailwindcss/vite@4.3.3(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0))': + '@tailwindcss/vite@4.3.3(vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0))': dependencies: '@tailwindcss/node': 4.3.3 '@tailwindcss/oxide': 4.3.3 tailwindcss: 4.3.3 - vite: 8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0) + vite: 8.2.1(@types/node@26.2.0)(jiti@2.7.0) '@tauri-apps/api@2.11.1': {} @@ -4808,10 +4558,10 @@ snapshots: '@ungap/structured-clone@1.3.3': {} - '@vitejs/plugin-react@6.0.5(vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0))': + '@vitejs/plugin-react@6.0.5(vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0))': dependencies: '@rolldown/pluginutils': 1.0.1 - vite: 8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0) + vite: 8.2.1(@types/node@26.2.0)(jiti@2.7.0) accepts@2.0.0: dependencies: @@ -5055,9 +4805,6 @@ snapshots: d3-timer@3.0.1: {} - date-fns@4.4.0: - optional: true - debounce-fn@4.0.0: dependencies: mimic-fn: 3.1.0 @@ -5171,36 +4918,6 @@ snapshots: es-toolkit@1.50.0: {} - esbuild@0.28.1: - optionalDependencies: - '@esbuild/aix-ppc64': 0.28.1 - '@esbuild/android-arm': 0.28.1 - '@esbuild/android-arm64': 0.28.1 - '@esbuild/android-x64': 0.28.1 - '@esbuild/darwin-arm64': 0.28.1 - '@esbuild/darwin-x64': 0.28.1 - '@esbuild/freebsd-arm64': 0.28.1 - '@esbuild/freebsd-x64': 0.28.1 - '@esbuild/linux-arm': 0.28.1 - '@esbuild/linux-arm64': 0.28.1 - '@esbuild/linux-ia32': 0.28.1 - '@esbuild/linux-loong64': 0.28.1 - '@esbuild/linux-mips64el': 0.28.1 - '@esbuild/linux-ppc64': 0.28.1 - '@esbuild/linux-riscv64': 0.28.1 - '@esbuild/linux-s390x': 0.28.1 - '@esbuild/linux-x64': 0.28.1 - '@esbuild/netbsd-arm64': 0.28.1 - '@esbuild/netbsd-x64': 0.28.1 - '@esbuild/openbsd-arm64': 0.28.1 - '@esbuild/openbsd-x64': 0.28.1 - '@esbuild/openharmony-arm64': 0.28.1 - '@esbuild/sunos-x64': 0.28.1 - '@esbuild/win32-arm64': 0.28.1 - '@esbuild/win32-ia32': 0.28.1 - '@esbuild/win32-x64': 0.28.1 - optional: true - escalade@3.2.0: {} escape-html@1.0.3: {} @@ -6538,7 +6255,7 @@ snapshots: react: 19.2.8 scheduler: 0.27.0 - react-is@19.2.7: {} + react-is@19.2.8: {} react-markdown@10.1.0(@types/react@19.2.18)(react@19.2.8): dependencies: @@ -6609,7 +6326,7 @@ snapshots: tiny-invariant: 1.3.3 tslib: 2.8.1 - recharts@3.10.1(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react-is@19.2.7)(react@19.2.8)(redux@5.0.1): + recharts@3.10.1(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react-is@19.2.8)(react@19.2.8)(redux@5.0.1): dependencies: '@reduxjs/toolkit': 2.12.0(react-redux@9.3.0(@types/react@19.2.18)(react@19.2.8)(redux@5.0.1))(react@19.2.8) clsx: 2.1.1 @@ -6619,7 +6336,7 @@ snapshots: immer: 11.1.16 react: 19.2.8 react-dom: 19.2.8(react@19.2.8) - react-is: 19.2.7 + react-is: 19.2.8 react-redux: 9.3.0(@types/react@19.2.18)(react@19.2.8)(redux@5.0.1) reselect: 5.2.0 tiny-invariant: 1.3.3 @@ -7171,7 +6888,7 @@ snapshots: d3-time: 3.1.0 d3-timer: 3.0.1 - vite@8.2.1(@types/node@26.2.0)(esbuild@0.28.1)(jiti@2.7.0): + vite@8.2.1(@types/node@26.2.0)(jiti@2.7.0): dependencies: lightningcss: 1.33.0 picomatch: 4.0.5 @@ -7180,7 +6897,6 @@ snapshots: tinyglobby: 0.2.17 optionalDependencies: '@types/node': 26.2.0 - esbuild: 0.28.1 fsevents: 2.3.3 jiti: 2.7.0 From 03325797bf995a10de9434b21349c0deb79cec70 Mon Sep 17 00:00:00 2001 From: Alois Date: Mon, 10 Aug 2026 00:07:22 +0200 Subject: [PATCH 05/13] fix(markdown): replace annotation with edited indicator --- package.json | 2 +- src/markdown/markdown.tsx | 48 ++++-------------------------- src/markdown/remark-small-muted.ts | 45 ---------------------------- src/markdown/text.tsx | 18 ++++++++++- 4 files changed, 23 insertions(+), 90 deletions(-) delete mode 100644 src/markdown/remark-small-muted.ts diff --git a/package.json b/package.json index 1682bf8..914b420 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@methanium/ui", - "version": "0.0.27", + "version": "0.0.28", "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/src/markdown/markdown.tsx b/src/markdown/markdown.tsx index 1176c38..a7848bb 100644 --- a/src/markdown/markdown.tsx +++ b/src/markdown/markdown.tsx @@ -6,7 +6,7 @@ import rehypeSlug from "rehype-slug"; import remarkBreaks from "remark-breaks"; import remarkGfm from "remark-gfm"; import remarkMath from "remark-math"; -import type { ComponentProps } from "react"; +import type { ComponentProps, ReactNode } from "react"; import { cn } from "../lib/utils"; import { CodeBlock } from "./code-block"; @@ -38,7 +38,6 @@ import { EmojiProvider, useEmojiRegistry } from "./emoji-context"; import type { EmojiRegistry } from "./emoji-data"; import { parseCodeMeta, remarkCodeMeta } from "./markdown-meta"; import { remarkEmoji } from "./remark-emoji"; -import { remarkSmallMuted } from "./remark-small-muted"; export type MarkdownTheme = "light" | "dark"; export type MarkdownVariant = "article" | "compact"; @@ -48,6 +47,7 @@ export type MarkdownProps = Omit, "children"> & { components?: Partial; registry?: EmojiRegistry; theme?: MarkdownTheme; + trailingContent?: ReactNode; variant?: MarkdownVariant; }; @@ -81,45 +81,6 @@ function classNames(node: Element) { return value ? [String(value)] : []; } -function moveFenceNotes(content: string) { - const lines = content.split("\n"); - const fence = /^ {0,3}`{3,}/; - const closingFence = /^ {0,3}`{3,}\s*$/; - const note = /\?\?\+\+.*?\?\?\+\+/g; - let inCodeBlock = false; - let openingIndex = -1; - let openingNotes: string[] = []; - - for (let index = 0; index < lines.length; index += 1) { - const line = lines[index]; - if (inCodeBlock) { - const closingNotes = line.match(note) ?? []; - const cleanedLine = line.replace(note, "").trimEnd(); - if (closingFence.test(cleanedLine)) { - if (openingNotes.length > 0) { - lines[openingIndex] = lines[openingIndex].replace(note, "").trimEnd(); - } - lines[index] = cleanedLine; - inCodeBlock = false; - const notes = [...openingNotes, ...closingNotes]; - if (notes.length > 0) { - lines.splice(index + 1, 0, ...notes); - index += notes.length; - } - openingNotes = []; - } - continue; - } - if (!fence.test(line)) continue; - - inCodeBlock = true; - openingIndex = index; - openingNotes = line.match(note) ?? []; - } - - return lines.join("\n"); -} - const defaultComponents: Components = { a: MarkdownLink, blockquote: MarkdownBlockquote, @@ -176,6 +137,7 @@ function Markdown({ components, registry: registryOverride, theme, + trailingContent, variant = "article", ...props }: MarkdownProps) { @@ -187,7 +149,6 @@ function Markdown({ remarkMath, remarkBreaks, remarkCodeMeta, - remarkSmallMuted, [remarkEmoji, { registry }], ]; @@ -210,8 +171,9 @@ function Markdown({ rehypePlugins={rehypePlugins} components={{ ...defaultComponents, ...components }} > - {moveFenceNotes(content)} + {content} + {trailingContent} ); diff --git a/src/markdown/remark-small-muted.ts b/src/markdown/remark-small-muted.ts deleted file mode 100644 index 6beadd1..0000000 --- a/src/markdown/remark-small-muted.ts +++ /dev/null @@ -1,45 +0,0 @@ -import type { Emphasis, Parent, Root, Text } from "mdast"; -import { SKIP, visit } from "unist-util-visit"; - -const delimiter = "??++"; - -export function remarkSmallMuted() { - return (tree: Root) => { - visit(tree, "text", (node: Text, index, parent: Parent | undefined) => { - if (index === undefined || !parent) return; - - const children: Parent["children"] = []; - let cursor = 0; - let opening = node.value.indexOf(delimiter); - - while (opening !== -1) { - const contentStart = opening + delimiter.length; - const closing = node.value.indexOf(delimiter, contentStart); - if (closing === -1) break; - - if (opening > cursor) { - children.push({ type: "text", value: node.value.slice(cursor, opening) }); - } - children.push({ - type: "emphasis", - children: [{ type: "text", value: node.value.slice(contentStart, closing) }], - data: { - hName: "span", - hProperties: { className: ["text-xs", "text-muted-foreground"] }, - }, - } satisfies Emphasis); - - cursor = closing + delimiter.length; - opening = node.value.indexOf(delimiter, cursor); - } - - if (children.length === 0) return; - if (cursor < node.value.length) { - children.push({ type: "text", value: node.value.slice(cursor) }); - } - - parent.children.splice(index, 1, ...children); - return [SKIP, index + children.length]; - }); - }; -} diff --git a/src/markdown/text.tsx b/src/markdown/text.tsx index 8615b39..bae9b5a 100644 --- a/src/markdown/text.tsx +++ b/src/markdown/text.tsx @@ -6,14 +6,30 @@ import { Markdown } from "./markdown"; export type TextProps = { fontSize?: CSSProperties["fontSize"]; registry?: EmojiRegistry; + showEditedIndicator?: boolean; value: string; }; -export default function Text({ fontSize, registry, value }: TextProps) { +export default function Text({ + fontSize, + registry, + showEditedIndicator = false, + value, +}: TextProps) { return ( p:nth-last-child(2)]:my-0 [&>p:nth-last-child(2)]:inline" + : undefined + } content={value} registry={registry} + trailingContent={ + showEditedIndicator ? ( + (edited) + ) : undefined + } variant="compact" style={{ fontSize: fontSize ?? "1rem" }} /> From 25fd556c3db700eab63548304eefa341d9a277e6 Mon Sep 17 00:00:00 2001 From: Alois Date: Mon, 10 Aug 2026 22:33:27 +0200 Subject: [PATCH 06/13] feat(legal): add privacy policy notice --- apps/showcase/src/App.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/apps/showcase/src/App.tsx b/apps/showcase/src/App.tsx index f67cc0d..8657a88 100644 --- a/apps/showcase/src/App.tsx +++ b/apps/showcase/src/App.tsx @@ -17,6 +17,7 @@ import { SelectValue, Sidebar, SidebarContent, + SidebarFooter, SidebarGroup, SidebarGroupContent, SidebarInset, @@ -205,6 +206,15 @@ export default function App() { + + + Privacy Policy + + From 56891acfe20c07e42b46231880468cd16a678df9 Mon Sep 17 00:00:00 2001 From: Alois Date: Mon, 10 Aug 2026 22:33:54 +0200 Subject: [PATCH 07/13] chore(deps): update codebase --- package.json | 6 +++--- pnpm-lock.yaml | 44 +++++++++++++++++++++++++------------------- 2 files changed, 28 insertions(+), 22 deletions(-) diff --git a/package.json b/package.json index 914b420..8779d0d 100644 --- a/package.json +++ b/package.json @@ -66,8 +66,8 @@ "emojibase-data": "^17.0.0", "hast-util-to-jsx-runtime": "^2.3.6", "input-otp": "^1.4.2", - "katex": "^0.18.2", - "lucide-react": "^1.30.0", + "katex": "^0.18.3", + "lucide-react": "^1.31.0", "next-themes": "^0.4.6", "react-markdown": "^10.1.0", "react-resizable-panels": "^4.12.2", @@ -80,7 +80,7 @@ "remark-math": "^6.0.0", "shadcn": "^4.16.2", "shiki": "^4.4.2", - "sonner": "^2.0.7", + "sonner": "^2.0.8", "tailwind-merge": "^3.6.0", "tw-animate-css": "^1.4.0", "unist-util-visit": "^5.1.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6fab24d..25f06d9 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -57,11 +57,11 @@ importers: specifier: ^1.4.2 version: 1.4.2(react-dom@19.2.8(react@19.2.8))(react@19.2.8) katex: - specifier: ^0.18.2 - version: 0.18.2 + specifier: ^0.18.3 + version: 0.18.3 lucide-react: - specifier: ^1.30.0 - version: 1.30.0(react@19.2.8) + specifier: ^1.31.0 + version: 1.31.0(react@19.2.8) next-themes: specifier: ^0.4.6 version: 0.4.6(react-dom@19.2.8(react@19.2.8))(react@19.2.8) @@ -105,8 +105,8 @@ importers: specifier: ^4.4.2 version: 4.4.2 sonner: - specifier: ^2.0.7 - version: 2.0.7(react-dom@19.2.8(react@19.2.8))(react@19.2.8) + specifier: ^2.0.8 + version: 2.0.8(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8) tailwind-merge: specifier: ^3.6.0 version: 3.6.0 @@ -1689,8 +1689,8 @@ packages: ee-first@1.1.1: resolution: {integrity: sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==} - electron-to-chromium@1.5.402: - resolution: {integrity: sha512-/oOpMaPT6Yg+6/1XQhyIPlzgj7Ye9zf+nNM2Uh6OcE2G2oNptWazFa+qB2Pdqqbsc9KnIDzgAntoYN0dbwOXwA==} + electron-to-chromium@1.5.403: + resolution: {integrity: sha512-MQsYmdaLzvaCX5j+ZZBr5Fm6uCCnPQcRtlvmvRlWqrXy+BH2O4ffXIAScF+JQznQWB9brWp4lSD9Z4yNmaf2BA==} embla-carousel-react@8.6.0: resolution: {integrity: sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA==} @@ -2277,8 +2277,8 @@ packages: resolution: {integrity: sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==} hasBin: true - katex@0.18.2: - resolution: {integrity: sha512-3snve4y0SXTMequLim1FMiPvLGElySam0blN5xQZBqEY3lOJz1TnuaaiugnBZBqHzlSAGmFTYL7MCQkORuLKCA==} + katex@0.18.3: + resolution: {integrity: sha512-6kbPr8KiYZpRfNNIw+8Td33njnFzK/ELcw6bI9FzIHysYPatAUdepQimfCX1vG2VCD/5Lv+Gu6P5YA58k8HHmw==} hasBin: true keyv@4.5.4: @@ -2465,8 +2465,8 @@ packages: lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} - lucide-react@1.30.0: - resolution: {integrity: sha512-tUIr2jXLbWpCkdtH8XP7P7YppM9ueWgTky99lpWDY6z5REs6B+O6ZQ3U5tHkUUY59ANyOv/PBcs8E4Fe3KO3eA==} + lucide-react@1.31.0: + resolution: {integrity: sha512-G8u2eEtoHUnUa9f8lbvqDhCiORMnYLdUEo06EEG9MQvHQrInKcX3Pa2TH39MM5qyzRcWETxB0+aOwAPI1g1kEg==} peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 @@ -3112,11 +3112,15 @@ packages: sisteransi@1.0.5: resolution: {integrity: sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==} - sonner@2.0.7: - resolution: {integrity: sha512-W6ZN4p58k8aDKA4XPcx2hpIQXBRAgyiWVkYhT7CvK6D3iAu7xjvVyhQHg2/iaKJZ1XVJ4r7XuwGL+WGEK37i9w==} + sonner@2.0.8: + resolution: {integrity: sha512-UM/ByIoFra8yzV75n1o0Puu0bw5U/9UNnDacrJNspekBewIfsQ3D6ez1nvlWpt7aTsO6rujQtifBpycwIivqlg==} peerDependencies: + '@types/react': ^18.0.0 || ^19.0.0 react: ^18.0.0 || ^19.0.0 || ^19.0.0-rc react-dom: ^18.0.0 || ^19.0.0 || ^19.0.0-rc + peerDependenciesMeta: + '@types/react': + optional: true source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} @@ -4646,7 +4650,7 @@ snapshots: dependencies: baseline-browser-mapping: 2.11.13 caniuse-lite: 1.0.30001809 - electron-to-chromium: 1.5.402 + electron-to-chromium: 1.5.403 node-releases: 2.0.53 update-browserslist-db: 1.3.0(browserslist@4.28.8) @@ -4866,7 +4870,7 @@ snapshots: ee-first@1.1.1: {} - electron-to-chromium@1.5.402: {} + electron-to-chromium@1.5.403: {} embla-carousel-react@8.6.0(react@19.2.8): dependencies: @@ -5485,7 +5489,7 @@ snapshots: dependencies: commander: 8.3.0 - katex@0.18.2: + katex@0.18.3: dependencies: commander: 8.3.0 @@ -5622,7 +5626,7 @@ snapshots: dependencies: yallist: 3.1.1 - lucide-react@1.30.0(react@19.2.8): + lucide-react@1.31.0(react@19.2.8): dependencies: react: 19.2.8 @@ -6613,10 +6617,12 @@ snapshots: sisteransi@1.0.5: {} - sonner@2.0.7(react-dom@19.2.8(react@19.2.8))(react@19.2.8): + sonner@2.0.8(@types/react@19.2.18)(react-dom@19.2.8(react@19.2.8))(react@19.2.8): dependencies: react: 19.2.8 react-dom: 19.2.8(react@19.2.8) + optionalDependencies: + '@types/react': 19.2.18 source-map-js@1.2.1: {} From e4c2d705e8d418b24ab8269f47eafeae95af5c9c Mon Sep 17 00:00:00 2001 From: Alois Date: Tue, 11 Aug 2026 20:36:07 +0200 Subject: [PATCH 08/13] feat(mobile): add safe area padding --- apps/showcase/index.html | 5 ++++- package.json | 2 +- src/cmp/alert-dialog.tsx | 2 +- src/cmp/combobox.tsx | 12 ++++++++++- src/cmp/command.tsx | 2 +- src/cmp/context-menu.tsx | 7 ++++++- src/cmp/dialog.tsx | 2 +- src/cmp/drawer.tsx | 2 +- src/cmp/dropdown-menu.tsx | 7 ++++++- src/cmp/hover-card.tsx | 7 ++++++- src/cmp/navigation-menu.tsx | 5 +++++ src/cmp/onboarding-flow.tsx | 2 +- src/cmp/popover.tsx | 7 ++++++- src/cmp/select.tsx | 12 ++++++++++- src/cmp/sheet.tsx | 4 ++-- src/cmp/sidebar.tsx | 4 ++-- src/cmp/sonner.tsx | 12 +++++++++++ src/cmp/tooltip.tsx | 7 ++++++- src/hooks/use-safe-area.ts | 40 +++++++++++++++++++++++++++++++++++++ src/index.css | 4 ++++ src/index.ts | 1 + src/screens/util.tsx | 8 +++----- 22 files changed, 131 insertions(+), 23 deletions(-) create mode 100644 src/hooks/use-safe-area.ts diff --git a/apps/showcase/index.html b/apps/showcase/index.html index 0226942..e03a0c5 100644 --- a/apps/showcase/index.html +++ b/apps/showcase/index.html @@ -2,7 +2,10 @@ - + Methanium UI Showcase diff --git a/package.json b/package.json index 8779d0d..1e336be 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@methanium/ui", - "version": "0.0.28", + "version": "0.0.29", "type": "module", "main": "./dist/index.js", "types": "./dist/index.d.ts", diff --git a/src/cmp/alert-dialog.tsx b/src/cmp/alert-dialog.tsx index a0a9c26..848b7a9 100644 --- a/src/cmp/alert-dialog.tsx +++ b/src/cmp/alert-dialog.tsx @@ -52,7 +52,7 @@ function AlertDialogContent({ data-slot="alert-dialog-content" data-size={size} className={cn( - "group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 gap-4 rounded-xl bg-popover p-4 text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", + "group/alert-dialog-content fixed top-[calc((100dvh+max(1rem,var(--ui-safe-area-top))-max(1rem,var(--ui-safe-area-bottom)))/2)] left-[calc((100%+max(1rem,var(--ui-safe-area-left))-max(1rem,var(--ui-safe-area-right)))/2)] z-50 grid w-[calc(100%-max(1rem,var(--ui-safe-area-left))-max(1rem,var(--ui-safe-area-right)))] max-h-[calc(100dvh-max(1rem,var(--ui-safe-area-top))-max(1rem,var(--ui-safe-area-bottom)))] -translate-x-1/2 -translate-y-1/2 gap-4 overflow-y-auto rounded-xl bg-popover p-4 text-popover-foreground ring-1 ring-foreground/10 duration-100 outline-none data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95", className, )} {...props} diff --git a/src/cmp/combobox.tsx b/src/cmp/combobox.tsx index dddb01f..755cc5d 100644 --- a/src/cmp/combobox.tsx +++ b/src/cmp/combobox.tsx @@ -2,6 +2,7 @@ import * as React from "react"; import { Combobox as ComboboxPrimitive } from "@base-ui/react"; import { cn } from "../lib/utils"; +import { useSafeAreaCollisionPadding } from "../hooks/use-safe-area"; import { Button } from "./button"; import { InputGroup, @@ -89,12 +90,20 @@ function ComboboxContent({ align = "start", alignOffset = 0, anchor, + collisionPadding, ...props }: ComboboxPrimitive.Popup.Props & Pick< ComboboxPrimitive.Positioner.Props, - "side" | "align" | "sideOffset" | "alignOffset" | "anchor" + | "side" + | "align" + | "sideOffset" + | "alignOffset" + | "anchor" + | "collisionPadding" >) { + const safeAreaCollisionPadding = useSafeAreaCollisionPadding(); + return ( ) { + const safeAreaCollisionPadding = useSafeAreaCollisionPadding(); + return ( ) { + const safeAreaCollisionPadding = useSafeAreaCollisionPadding(); + return ( ; @@ -20,12 +21,15 @@ function HoverCardContent({ sideOffset = 4, align = "center", alignOffset = 4, + collisionPadding, ...props }: PreviewCardPrimitive.Popup.Props & Pick< PreviewCardPrimitive.Positioner.Props, - "align" | "alignOffset" | "side" | "sideOffset" + "align" | "alignOffset" | "side" | "sideOffset" | "collisionPadding" >) { + const safeAreaCollisionPadding = useSafeAreaCollisionPadding(); + return ( -