From 4e82a2a21c0cac20767bfdb0940199514f843ba0 Mon Sep 17 00:00:00 2001 From: juprodh Date: Fri, 21 Aug 2026 13:29:40 +0800 Subject: [PATCH] Share notes to other than Bluesky atmosphere clients Signed-off-by: juprodh --- bun.lock | 3 ++ package.json | 1 + src/server/routes/render-note.ts | 4 +- src/views/icons.ts | 1 + src/views/layout.ts | 19 ++++--- src/views/share.ts | 51 ++++++++++++++----- src/views/theme/index.ts | 3 ++ src/views/theme/tokens.ts | 7 +++ tests/views/share.test.ts | 86 ++++++++++++++++++++++++++++++++ 9 files changed, 151 insertions(+), 24 deletions(-) create mode 100644 tests/views/share.test.ts diff --git a/bun.lock b/bun.lock index 5aa5888..668c6ac 100644 --- a/bun.lock +++ b/bun.lock @@ -14,6 +14,7 @@ "@atcute/lexicons": "^2.0.3", "@atcute/oauth-node-client": "^2.0.1", "@atcute/tid": "^1.1.4", + "@aturi.to/waypoints": "0.1.3", "@elysiajs/static": "^1.4.10", "@sindresorhus/slugify": "^3.0.0", "diff": "^9.0.0", @@ -96,6 +97,8 @@ "@atcute/util-text": ["@atcute/util-text@1.3.4", "", { "dependencies": { "unicode-segmenter": "^0.17.0" } }, "sha512-u2UAM7iSM09sQaSG9jtxWFSPgB8boVj50/BoyMvYnhVgGBu+nXIuAcdDUQCsZA44YZgTPPhN/b86JX+jH7SPzQ=="], + "@aturi.to/waypoints": ["@aturi.to/waypoints@0.1.3", "", {}, "sha512-WZ2BcBkQCMrKSsMrC+HpbMkAsvmQohOqe+hU5IdKdnqYhpM6SDipHsaMu6MYeZUKDsKS7a16yP+ztCXS1GeuSA=="], + "@biomejs/biome": ["@biomejs/biome@2.5.8", "", { "optionalDependencies": { "@biomejs/cli-darwin-arm64": "2.5.8", "@biomejs/cli-darwin-x64": "2.5.8", "@biomejs/cli-linux-arm64": "2.5.8", "@biomejs/cli-linux-arm64-musl": "2.5.8", "@biomejs/cli-linux-x64": "2.5.8", "@biomejs/cli-linux-x64-musl": "2.5.8", "@biomejs/cli-win32-arm64": "2.5.8", "@biomejs/cli-win32-x64": "2.5.8" }, "bin": { "biome": "bin/biome" } }, "sha512-aeAeeJB9fSDc7Gq+2GqpQxA0qBj6gj1k2R6L1cYqGePKP/baIq1WX8y6B+D+nRsO5ViQL22K/8IwbqERW0q1nw=="], "@biomejs/cli-darwin-arm64": ["@biomejs/cli-darwin-arm64@2.5.8", "", { "os": "darwin", "cpu": "arm64" }, "sha512-mk1QON9PHllvvLN5gU3f4rMxeh4syK5p9OvKyWH6/W8ueh04uaC8TUXXByhGufWf/y5mQc03ZLM45zU+cmqMjA=="], diff --git a/package.json b/package.json index bc12c49..bcaa6c2 100644 --- a/package.json +++ b/package.json @@ -54,6 +54,7 @@ "@atcute/lexicons": "^2.0.3", "@atcute/oauth-node-client": "^2.0.1", "@atcute/tid": "^1.1.4", + "@aturi.to/waypoints": "0.1.3", "@elysiajs/static": "^1.4.10", "@sindresorhus/slugify": "^3.0.0", "diff": "^9.0.0", diff --git a/src/server/routes/render-note.ts b/src/server/routes/render-note.ts index da25ce7..68ee8ba 100644 --- a/src/server/routes/render-note.ts +++ b/src/server/routes/render-note.ts @@ -6,7 +6,7 @@ import { htmlResponse } from "../../lib/response.ts"; import { noteUrl, resolvePublicUrl } from "../../lib/urls.ts"; import { resolveBookmarkHtml } from "../../views/bookmark.ts"; import { notePage } from "../../views/note.ts"; -import { shareOnBlueskyButton } from "../../views/share.ts"; +import { shareMenu } from "../../views/share.ts"; import { atCanonicalOption, wikiIdentity, @@ -37,7 +37,7 @@ export function renderNoteResponse(input: NoteRenderInput): Response { const bmHtml = resolveBookmarkHtml(ctx.did, ctx.wiki.at_uri, msg); const publicUrl = resolvePublicUrl(request); const canonicalUrl = `${publicUrl}${noteUrl(handle, ctx.wiki.slug, noteSlug)}`; - const shareHtml = shareOnBlueskyButton(noteTitle, canonicalUrl, msg); + const shareHtml = shareMenu(noteTitle, canonicalUrl, msg); const ogImage = `${publicUrl}/og/@${handle}/${ctx.wiki.slug}`; const ogDescription = ctx.wiki.description ? { ogDescription: ctx.wiki.description } diff --git a/src/views/icons.ts b/src/views/icons.ts index 6ef118a..5a909f0 100644 --- a/src/views/icons.ts +++ b/src/views/icons.ts @@ -12,6 +12,7 @@ export const ICONS = { theme: ``, shield: ``, users: ``, + share: ``, link: ``, // Inlined for fill="currentColor" (follows theme accent); geometry matches /public/logo.svg. logo: ``, diff --git a/src/views/layout.ts b/src/views/layout.ts index 70c9cba..c2486d4 100644 --- a/src/views/layout.ts +++ b/src/views/layout.ts @@ -21,6 +21,9 @@ import { dangerSmallButtonClass, fieldFocusClass, kbdClass, + menuItemClass, + menuItemFocusClass, + menuPanelClass, outlineSmallButtonClass, primarySmallButtonClass, sidebarButtonClass, @@ -42,9 +45,6 @@ const LOCALE_LABELS: Record = { ja: "日本語", }; -const MENU_ITEM_FOCUS = - "focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-inset focus-visible:ring-[var(--accent-focus)]"; - export interface LayoutOptions { scripts?: string[]; stylesheets?: string[]; @@ -172,11 +172,10 @@ function renderWithSidebar(body: string, options: LayoutOptions): string { ? `
${noteActions.join("\n")}
` : ""; - const dropdownItemClass = `block px-3 py-1.5 text-sm ${THEME.textSecondary} ${THEME.accentSoft} ${MENU_ITEM_FOCUS} rounded`; const noteExportItem = options.currentNoteSlug - ? `${msg.wiki.exportNote}` + ? `${msg.wiki.exportNote}` : ""; - const wikiExportItem = `${msg.wiki.exportWiki}`; + const wikiExportItem = `${msg.wiki.exportWiki}`; const historyLink = options.currentNoteSlug ? ` @@ -190,7 +189,7 @@ function renderWithSidebar(body: string, options: LayoutOptions): string { ${ICONS.download} ${msg.wiki.export} -