mirror of
https://github.com/supermemoryai/supermemory.git
synced 2026-08-28 05:25:33 +00:00
Merge 16b27f66ef into 3f7b9667c6
This commit is contained in:
commit
0d343e53b2
3 changed files with 110 additions and 1 deletions
|
|
@ -18,6 +18,12 @@ import { useLocalStorageUsername } from "@hooks/use-local-storage-username"
|
|||
import { useHasCompanyBrain } from "@/hooks/use-company-brain"
|
||||
import { toast } from "sonner"
|
||||
import * as htmlToImage from "html-to-image"
|
||||
import {
|
||||
getShareAspectPreset,
|
||||
SHARE_ASPECT_PRESETS,
|
||||
type ShareAspect,
|
||||
shareAspectMaxHeightDvh,
|
||||
} from "@/lib/share-aspect"
|
||||
|
||||
type BackgroundTheme = "gradient" | "dark-gradient" | "black"
|
||||
|
||||
|
|
@ -281,9 +287,11 @@ export function ShareModal({
|
|||
const isCompanyBrain = useHasCompanyBrain()
|
||||
const [selectedTheme, setSelectedTheme] =
|
||||
useState<BackgroundTheme>("gradient")
|
||||
const [selectedAspect, setSelectedAspect] = useState<ShareAspect>("post")
|
||||
const [isCopying, setIsCopying] = useState(false)
|
||||
const [copied, setCopied] = useState(false)
|
||||
const previewRef = useRef<HTMLDivElement>(null)
|
||||
const aspectPreset = getShareAspectPreset(selectedAspect)
|
||||
|
||||
const localStorageUsername = useLocalStorageUsername()
|
||||
const displayName =
|
||||
|
|
@ -438,8 +446,10 @@ export function ShareModal({
|
|||
{/* Preview area */}
|
||||
<div
|
||||
ref={previewRef}
|
||||
className="relative w-full aspect-[674/505] max-h-[48dvh] rounded-[14px] overflow-hidden"
|
||||
className="relative mx-auto w-full rounded-[14px] overflow-hidden"
|
||||
style={{
|
||||
aspectRatio: aspectPreset.ratio,
|
||||
maxHeight: `${shareAspectMaxHeightDvh(selectedAspect)}dvh`,
|
||||
boxShadow: "inset 2.42px 2.42px 4.26316px rgba(11, 15, 21, 0.7)",
|
||||
}}
|
||||
>
|
||||
|
|
@ -493,6 +503,26 @@ export function ShareModal({
|
|||
isSelected={selectedTheme === "black"}
|
||||
onClick={() => setSelectedTheme("black")}
|
||||
/>
|
||||
<div className="mx-1 h-5 w-px bg-white/10" aria-hidden />
|
||||
<div className="flex items-center gap-1 rounded-full bg-[#0D121A] p-0.5">
|
||||
{SHARE_ASPECT_PRESETS.map((preset) => (
|
||||
<button
|
||||
key={preset.id}
|
||||
type="button"
|
||||
aria-pressed={selectedAspect === preset.id}
|
||||
onClick={() => setSelectedAspect(preset.id)}
|
||||
className={cn(
|
||||
dmSansClassName(),
|
||||
"h-6 rounded-full px-2.5 text-[11px] font-medium transition-colors",
|
||||
selectedAspect === preset.id
|
||||
? "bg-white/[0.12] text-white"
|
||||
: "text-[#8B929E] hover:text-white",
|
||||
)}
|
||||
>
|
||||
{preset.label}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Action buttons */}
|
||||
|
|
|
|||
39
apps/web/lib/share-aspect.test.ts
Normal file
39
apps/web/lib/share-aspect.test.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import { describe, expect, it } from "bun:test"
|
||||
import {
|
||||
getShareAspectPreset,
|
||||
SHARE_ASPECT_PRESETS,
|
||||
shareAspectMaxHeightDvh,
|
||||
type ShareAspect,
|
||||
} from "./share-aspect"
|
||||
|
||||
describe("share-aspect presets", () => {
|
||||
it("exposes post, square and story", () => {
|
||||
expect(SHARE_ASPECT_PRESETS.map((p) => p.id)).toEqual([
|
||||
"post",
|
||||
"square",
|
||||
"story",
|
||||
])
|
||||
})
|
||||
|
||||
it("resolves each preset by id", () => {
|
||||
for (const preset of SHARE_ASPECT_PRESETS) {
|
||||
expect(getShareAspectPreset(preset.id)).toBe(preset)
|
||||
}
|
||||
})
|
||||
|
||||
it("falls back to post for an unknown aspect", () => {
|
||||
expect(getShareAspectPreset("nope" as ShareAspect).id).toBe("post")
|
||||
})
|
||||
|
||||
it("has coherent ratio strings and numeric values", () => {
|
||||
expect(getShareAspectPreset("square").value).toBe(1)
|
||||
expect(getShareAspectPreset("post").value).toBeGreaterThan(1) // landscape
|
||||
expect(getShareAspectPreset("story").value).toBeLessThan(1) // portrait
|
||||
})
|
||||
|
||||
it("lets the narrow portrait story use more vertical space than the wide post", () => {
|
||||
expect(shareAspectMaxHeightDvh("story")).toBeGreaterThan(
|
||||
shareAspectMaxHeightDvh("post"),
|
||||
)
|
||||
})
|
||||
})
|
||||
40
apps/web/lib/share-aspect.ts
Normal file
40
apps/web/lib/share-aspect.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/**
|
||||
* Aspect-ratio presets for the graph share card, sized for where people post
|
||||
* screenshots. Pure so the mapping can be unit tested.
|
||||
*/
|
||||
|
||||
export type ShareAspect = "post" | "square" | "story"
|
||||
|
||||
export interface ShareAspectPreset {
|
||||
id: ShareAspect
|
||||
label: string
|
||||
/** CSS `aspect-ratio` value (width / height). */
|
||||
ratio: string
|
||||
/** Width / height as a number, for any numeric layout math. */
|
||||
value: number
|
||||
}
|
||||
|
||||
const POST_PRESET: ShareAspectPreset = {
|
||||
id: "post",
|
||||
label: "Post",
|
||||
ratio: "674 / 505",
|
||||
value: 674 / 505,
|
||||
}
|
||||
|
||||
export const SHARE_ASPECT_PRESETS: readonly ShareAspectPreset[] = [
|
||||
POST_PRESET,
|
||||
{ id: "square", label: "Square", ratio: "1 / 1", value: 1 },
|
||||
{ id: "story", label: "Story", ratio: "9 / 16", value: 9 / 16 },
|
||||
]
|
||||
|
||||
export function getShareAspectPreset(aspect: ShareAspect): ShareAspectPreset {
|
||||
return SHARE_ASPECT_PRESETS.find((p) => p.id === aspect) ?? POST_PRESET
|
||||
}
|
||||
|
||||
/**
|
||||
* A max-height (in dvh) that keeps the preview inside the dialog. Tall (story)
|
||||
* ratios need to be shorter than wide ones so they don't overflow.
|
||||
*/
|
||||
export function shareAspectMaxHeightDvh(aspect: ShareAspect): number {
|
||||
return aspect === "story" ? 60 : 48
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue