A social knowledge tool for researchers built on ATProto

feat: bookmarklet layout

Changed files
+97 -55
src
webapp
app
bookmarklet
+15
src/webapp/app/bookmarklet/layout.tsx
···
··· 1 + import type { Metadata } from 'next'; 2 + 3 + export const metadata: Metadata = { 4 + title: 'Semble bookmarklet', 5 + description: 6 + 'Learn how to add our bookmarklet to your browser to quickly open any webpage in Semble.', 7 + }; 8 + 9 + interface Props { 10 + children: React.ReactNode; 11 + } 12 + 13 + export default function Layout(props: Props) { 14 + return props.children; 15 + }
+82 -55
src/webapp/app/bookmarklet/page.tsx
··· 9 Code, 10 Alert, 11 Box, 12 Group, 13 } from '@mantine/core'; 14 import { useState } from 'react'; 15 import { BiInfoCircle } from 'react-icons/bi'; 16 17 export default function BookmarkletPage() { 18 - const [copied, setCopied] = useState(false); 19 - 20 const appUrl = process.env.NEXT_PUBLIC_APP_URL || 'http://127.0.0.1:4000'; 21 22 const bookmarkletCode = `javascript:(function(){ 23 const currentUrl = window.location.href; 24 const sembleUrl = '${appUrl}/url?id=' + currentUrl; 25 window.open(sembleUrl, '_blank'); 26 - })();`; 27 - 28 - const handleCopy = async () => { 29 - try { 30 - await navigator.clipboard.writeText(bookmarkletCode); 31 - setCopied(true); 32 - setTimeout(() => setCopied(false), 2000); 33 - } catch (err) { 34 - console.error('Failed to copy bookmarklet:', err); 35 - } 36 - }; 37 38 // Create the bookmarklet link using dangerouslySetInnerHTML to bypass React's security check 39 const createBookmarkletLink = () => { 40 return { 41 - __html: `<a href="${bookmarkletCode}" style="text-decoration: none; padding: 8px 16px; background-color: var(--mantine-color-orange-6); color: white; border-radius: 4px; display: inline-flex; align-items: center; gap: 8px;"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2z"/></svg>Open in Semble</a>`, 42 }; 43 }; 44 45 return ( 46 - <Container size="md" py="xl"> 47 <Stack gap="xl"> 48 - <Stack gap="md"> 49 - <Title order={1}>Semble Bookmarklet</Title> 50 - <Text size="lg" c="dimmed"> 51 - Add this bookmarklet to your browser to quickly open any webpage in 52 - Semble. 53 - </Text> 54 </Stack> 55 56 - <Alert icon={<BiInfoCircle />} title="How to install" color="orange"> 57 <Stack gap="sm"> 58 - <Text> 59 - 1. Copy the bookmarklet code below or drag the button to your 60 - bookmarks bar 61 - </Text> 62 - <Text> 63 - { 64 - "2. When you're on any webpage, click the bookmarklet to open it in Semble" 65 - } 66 - </Text> 67 </Stack> 68 </Alert> 69 70 <Stack gap="md"> 71 - <Title order={2} size="h3"> 72 - Method 1: Drag to Bookmarks Bar 73 - </Title> 74 - <Text c="dimmed"> 75 - {"Drag this button directly to your browser's bookmarks bar:"} 76 - </Text> 77 <Group> 78 <Box dangerouslySetInnerHTML={createBookmarkletLink()} /> 79 </Group> 80 </Stack> 81 82 <Stack gap="md"> 83 - <Title order={2} size="h3"> 84 - Method 2: Copy Code 85 - </Title> 86 - <Text c="dimmed"> 87 - Copy this code and create a new bookmark with it as the URL: 88 - </Text> 89 <Box pos="relative"> 90 <Code 91 block ··· 98 > 99 {bookmarkletCode} 100 </Code> 101 - <Button 102 - size="xs" 103 - variant="light" 104 - onClick={handleCopy} 105 - style={{ 106 - position: 'absolute', 107 - top: '8px', 108 - right: '8px', 109 - }} 110 - > 111 - {copied ? 'Copied!' : 'Copy'} 112 - </Button> 113 </Box> 114 </Stack> 115 </Stack>
··· 9 Code, 10 Alert, 11 Box, 12 + Badge, 13 + Image, 14 Group, 15 + Anchor, 16 + CopyButton, 17 } from '@mantine/core'; 18 import { useState } from 'react'; 19 import { BiInfoCircle } from 'react-icons/bi'; 20 + import SembleLogo from '@/assets/semble-logo.svg'; 21 + import Link from 'next/link'; 22 23 export default function BookmarkletPage() { 24 const appUrl = process.env.NEXT_PUBLIC_APP_URL || 'http://127.0.0.1:4000'; 25 26 const bookmarkletCode = `javascript:(function(){ 27 const currentUrl = window.location.href; 28 const sembleUrl = '${appUrl}/url?id=' + currentUrl; 29 window.open(sembleUrl, '_blank'); 30 + })();`; 31 32 // Create the bookmarklet link using dangerouslySetInnerHTML to bypass React's security check 33 const createBookmarkletLink = () => { 34 return { 35 + __html: `<a href="${bookmarkletCode}" style="text-decoration: none; padding: 8px 16px; background-color: var(--mantine-color-tangerine-6); color: white; border-radius: 100px; display: inline-flex; align-items: center; gap: 8px; font-weight: 600;"><svg width="16" height="16" viewBox="0 0 24 24" fill="currentColor"><path d="M17 3H7c-1.1 0-1.99.9-1.99 2L5 21l7-3 7 3V5c0-1.1-.9-2-2-2z"/></svg>Open in Semble</a>`, 36 }; 37 }; 38 39 return ( 40 + <Container size="sm" p="md"> 41 <Stack gap="xl"> 42 + <Stack gap="xs" align="center"> 43 + <Stack align="center" gap={'xs'}> 44 + <Anchor component={Link} href={'/'}> 45 + <Image 46 + src={SembleLogo.src} 47 + alt="Semble logo" 48 + w={48} 49 + h={64.5} 50 + mx={'auto'} 51 + /> 52 + <Badge size="sm">Alpha</Badge> 53 + </Anchor> 54 + </Stack> 55 + <Stack gap={'xs'} align="center"> 56 + <Title order={1}>Semble Bookmarklet</Title> 57 + <Title 58 + order={2} 59 + size="xl" 60 + c="dimmed" 61 + fw={600} 62 + maw={500} 63 + ta={'center'} 64 + > 65 + Add this bookmarklet to your browser to quickly open any webpage 66 + in Semble. 67 + </Title> 68 + </Stack> 69 </Stack> 70 71 + <Alert title="How to install" color="grape"> 72 <Stack gap="sm"> 73 + <Group gap={'xs'}> 74 + <Badge size="md" color="grape" circle> 75 + 1 76 + </Badge> 77 + <Text fw={500} c="grape"> 78 + Copy the bookmarklet code below or drag the button to your 79 + bookmarks bar 80 + </Text> 81 + </Group> 82 + <Group gap={'xs'}> 83 + <Badge size="md" color="grape" circle> 84 + 2 85 + </Badge> 86 + 87 + <Text fw={500} c={'grape'}> 88 + { 89 + "When you're on any webpage, click the bookmarklet to open it in Semble" 90 + } 91 + </Text> 92 + </Group> 93 </Stack> 94 </Alert> 95 96 <Stack gap="md"> 97 + <Stack gap={'xs'}> 98 + <Title order={3}>Method 1: Drag to Bookmarks Bar</Title> 99 + <Text c="dimmed" fw={500}> 100 + {"Drag this button directly to your browser's bookmarks bar:"} 101 + </Text> 102 + </Stack> 103 <Group> 104 <Box dangerouslySetInnerHTML={createBookmarkletLink()} /> 105 </Group> 106 </Stack> 107 108 <Stack gap="md"> 109 + <Stack gap={'xs'}> 110 + <Title order={3}>Method 2: Copy Code</Title> 111 + <Text c="dimmed" fw={500}> 112 + Copy this code and create a new bookmark with it as the URL: 113 + </Text> 114 + </Stack> 115 <Box pos="relative"> 116 <Code 117 block ··· 124 > 125 {bookmarkletCode} 126 </Code> 127 + <CopyButton value={bookmarkletCode}> 128 + {({ copied, copy }) => ( 129 + <Button 130 + color="dark" 131 + pos={'absolute'} 132 + top={12} 133 + right={12} 134 + onClick={copy} 135 + > 136 + {copied ? 'Copied!' : 'Copy'} 137 + </Button> 138 + )} 139 + </CopyButton> 140 </Box> 141 </Stack> 142 </Stack>