aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDhravya Shah <[email protected]>2024-06-18 17:58:46 -0500
committerDhravya Shah <[email protected]>2024-06-18 17:58:46 -0500
commitf4bb71e8f7e07bb2e919b7f222d5acb2905eb8f2 (patch)
tree7310dc521ef3559055bbe71f50c3861be2fa0503
parentdarkmode by default - so that the colors don't f up on lightmode devices (diff)
parentCreate Embeddings for Canvas (diff)
downloadsupermemory-default-darkmode.tar.xz
supermemory-default-darkmode.zip
-rw-r--r--SETUP-GUIDE.md15
m---------apps/browser-rendering0
-rw-r--r--apps/cf-ai-backend/README.md74
-rw-r--r--apps/cf-ai-backend/src/helper.ts105
-rw-r--r--apps/cf-ai-backend/src/index.test.ts13
-rw-r--r--apps/cf-ai-backend/src/index.ts157
-rw-r--r--apps/cf-ai-backend/src/prompts/prompt1.ts8
-rw-r--r--apps/cf-ai-backend/src/types.ts5
-rw-r--r--apps/cf-ai-backend/src/utils/OpenAIEmbedder.ts24
-rw-r--r--apps/cf-ai-backend/src/utils/chonker.ts3
-rw-r--r--apps/cf-ai-backend/src/utils/seededRandom.ts7
-rw-r--r--apps/cf-ai-backend/tsconfig.json3
-rw-r--r--apps/cf-ai-backend/wrangler.toml2
-rw-r--r--apps/web/app/(auth)/auth-buttons.tsx2
-rw-r--r--apps/web/app/(auth)/signin/page.tsx28
-rw-r--r--apps/web/app/(canvas)/canvas.tsx55
-rw-r--r--apps/web/app/(canvas)/canvas/layout.tsx13
-rw-r--r--apps/web/app/(canvas)/canvas/page.tsx99
-rw-r--r--apps/web/app/(canvas)/canvasStyles.css24
-rw-r--r--apps/web/app/(canvas)/enabledComp.tsx22
-rw-r--r--apps/web/app/(canvas)/lib/createAssetUrl.ts94
-rw-r--r--apps/web/app/(canvas)/lib/createEmbeds.ts142
-rw-r--r--apps/web/app/(canvas)/lib/loadSnap.ts13
-rw-r--r--apps/web/app/(canvas)/savesnap.tsx43
-rw-r--r--apps/web/app/(canvas)/svg.tsx97
-rw-r--r--apps/web/app/(canvas)/twitterCard.tsx84
-rw-r--r--apps/web/app/(dash)/actions.ts48
-rw-r--r--apps/web/app/(dash)/chat/CodeBlock.tsx90
-rw-r--r--apps/web/app/(dash)/chat/actions.ts1
-rw-r--r--apps/web/app/(dash)/chat/chatWindow.tsx249
-rw-r--r--apps/web/app/(dash)/chat/markdownRenderHelpers.tsx25
-rw-r--r--apps/web/app/(dash)/chat/page.tsx6
-rw-r--r--apps/web/app/(dash)/dynamicisland.tsx373
-rw-r--r--apps/web/app/(dash)/header.tsx40
-rw-r--r--apps/web/app/(dash)/home/page.tsx15
-rw-r--r--apps/web/app/(dash)/home/queryinput.tsx23
-rw-r--r--apps/web/app/(dash)/layout.tsx9
-rw-r--r--apps/web/app/(dash)/memories/page.tsx133
-rw-r--r--apps/web/app/(dash)/menu.tsx21
-rw-r--r--apps/web/app/(editor)/ai.md43
-rw-r--r--apps/web/app/(editor)/components/aigenerate.tsx169
-rw-r--r--apps/web/app/(editor)/components/editorcommands.tsx85
-rw-r--r--apps/web/app/(editor)/components/extensions.ts141
-rw-r--r--apps/web/app/(editor)/components/image-upload.ts50
-rw-r--r--apps/web/app/(editor)/components/selectors/bgcolor-selector.tsx107
-rw-r--r--apps/web/app/(editor)/components/selectors/color-selector.tsx111
-rw-r--r--apps/web/app/(editor)/components/selectors/link-selector.tsx95
-rw-r--r--apps/web/app/(editor)/components/selectors/node-selector.tsx126
-rw-r--r--apps/web/app/(editor)/components/selectors/text-buttons.tsx63
-rw-r--r--apps/web/app/(editor)/components/slash-command.tsx163
-rw-r--r--apps/web/app/(editor)/components/topbar.tsx68
-rw-r--r--apps/web/app/(editor)/components/ui/asksvg.tsx12
-rw-r--r--apps/web/app/(editor)/components/ui/autocompletesvg.tsx12
-rw-r--r--apps/web/app/(editor)/components/ui/crazy-spinner.tsx11
-rw-r--r--apps/web/app/(editor)/components/ui/magic.tsx8
-rw-r--r--apps/web/app/(editor)/components/ui/rewritesvg.tsx11
-rw-r--r--apps/web/app/(editor)/components/ui/translatesvg.tsx12
-rw-r--r--apps/web/app/(editor)/editor.tsx54
-rw-r--r--apps/web/app/(editor)/editor/page.tsx8
-rw-r--r--apps/web/app/(editor)/layout.tsx12
-rw-r--r--apps/web/app/(editor)/lib/content.ts231
-rw-r--r--apps/web/app/(editor)/lib/debouncedsave.ts20
-rw-r--r--apps/web/app/(editor)/lib/editorprops.ts16
-rw-r--r--apps/web/app/(editor)/lib/use-local-storage.ts27
-rw-r--r--apps/web/app/(editor)/styles/globals.css168
-rw-r--r--apps/web/app/(editor)/styles/prosemirror.css203
-rw-r--r--apps/web/app/(landing)/package.json5
-rw-r--r--apps/web/app/(landing)/page.tsx4
-rw-r--r--apps/web/app/actions/doers.ts268
-rw-r--r--apps/web/app/actions/fetchers.ts142
-rw-r--r--apps/web/app/actions/types.ts11
-rw-r--r--apps/web/app/api/[...nextauth]/route.ts2
-rw-r--r--apps/web/app/api/chat/route.ts103
-rw-r--r--apps/web/app/api/editorai/route.ts20
-rw-r--r--apps/web/app/api/ensureAuth.ts4
-rw-r--r--apps/web/app/api/getCount/route.ts8
-rw-r--r--apps/web/app/api/me/route.ts4
-rw-r--r--apps/web/app/api/spaces/route.ts4
-rw-r--r--apps/web/app/api/store/route.ts31
-rw-r--r--apps/web/app/api/unfirlsite/route.ts134
-rw-r--r--apps/web/app/layout.tsx6
-rw-r--r--apps/web/app/ref/page.tsx8
-rw-r--r--apps/web/cf-env.d.ts13
-rw-r--r--apps/web/env.d.ts8
-rw-r--r--apps/web/lib/constants.ts43
-rw-r--r--apps/web/lib/get-metadata.ts (renamed from apps/web/app/helpers/lib/get-metadata.ts)0
-rw-r--r--apps/web/lib/get-theme-button.tsx (renamed from apps/web/app/helpers/lib/get-theme-button.tsx)0
-rw-r--r--apps/web/lib/handle-errors.ts (renamed from apps/web/app/helpers/lib/handle-errors.ts)0
-rw-r--r--apps/web/lib/searchParams.ts (renamed from apps/web/app/helpers/lib/searchParams.ts)0
-rw-r--r--apps/web/migrations/000_setup.sql55
-rw-r--r--apps/web/migrations/meta/0000_snapshot.json158
-rw-r--r--apps/web/migrations/meta/_journal.json4
-rw-r--r--apps/web/next.config.mjs1
-rw-r--r--apps/web/package.json10
-rw-r--r--apps/web/public/embed-icons/codepen.pngbin0 -> 2180 bytes
-rw-r--r--apps/web/public/embed-icons/codesandbox.pngbin0 -> 237 bytes
-rw-r--r--apps/web/public/embed-icons/desmos.pngbin0 -> 1425 bytes
-rw-r--r--apps/web/public/embed-icons/excalidraw.pngbin0 -> 846 bytes
-rw-r--r--apps/web/public/embed-icons/felt.pngbin0 -> 977 bytes
-rw-r--r--apps/web/public/embed-icons/figma.pngbin0 -> 1299 bytes
-rw-r--r--apps/web/public/embed-icons/github_gist.pngbin0 -> 1471 bytes
-rw-r--r--apps/web/public/embed-icons/google_calendar.pngbin0 -> 962 bytes
-rw-r--r--apps/web/public/embed-icons/google_maps.pngbin0 -> 1902 bytes
-rw-r--r--apps/web/public/embed-icons/google_slides.pngbin0 -> 1435 bytes
-rw-r--r--apps/web/public/embed-icons/observable.pngbin0 -> 769 bytes
-rw-r--r--apps/web/public/embed-icons/replit.pngbin0 -> 526 bytes
-rw-r--r--apps/web/public/embed-icons/scratch.pngbin0 -> 3716 bytes
-rw-r--r--apps/web/public/embed-icons/spotify.pngbin0 -> 2261 bytes
-rw-r--r--apps/web/public/embed-icons/tldraw.pngbin0 -> 625 bytes
-rw-r--r--apps/web/public/embed-icons/val_town.pngbin0 -> 540 bytes
-rw-r--r--apps/web/public/embed-icons/vimeo.pngbin0 -> 864 bytes
-rw-r--r--apps/web/public/embed-icons/youtube.pngbin0 -> 846 bytes
-rw-r--r--apps/web/public/fonts/IBMPlexMono-Medium.woff2bin0 -> 40396 bytes
-rw-r--r--apps/web/public/fonts/IBMPlexSans-Medium.woff2bin0 -> 63940 bytes
-rw-r--r--apps/web/public/fonts/IBMPlexSerif-Medium.woff2bin0 -> 59496 bytes
-rw-r--r--apps/web/public/fonts/Shantell_Sans-Tldrawish.woff2bin0 -> 152980 bytes
-rw-r--r--apps/web/public/icons/icon/align-bottom.svg3
-rw-r--r--apps/web/public/icons/icon/align-center-horizontal.svg4
-rw-r--r--apps/web/public/icons/icon/align-center-vertical.svg3
-rw-r--r--apps/web/public/icons/icon/align-left.svg4
-rw-r--r--apps/web/public/icons/icon/align-right.svg4
-rw-r--r--apps/web/public/icons/icon/align-top.svg3
-rw-r--r--apps/web/public/icons/icon/arrow-left.svg3
-rw-r--r--apps/web/public/icons/icon/arrowhead-arrow.svg3
-rw-r--r--apps/web/public/icons/icon/arrowhead-bar.svg3
-rw-r--r--apps/web/public/icons/icon/arrowhead-diamond.svg4
-rw-r--r--apps/web/public/icons/icon/arrowhead-dot.svg4
-rw-r--r--apps/web/public/icons/icon/arrowhead-none.svg3
-rw-r--r--apps/web/public/icons/icon/arrowhead-square.svg3
-rw-r--r--apps/web/public/icons/icon/arrowhead-triangle-inverted.svg3
-rw-r--r--apps/web/public/icons/icon/arrowhead-triangle.svg3
-rw-r--r--apps/web/public/icons/icon/blob.svg3
-rw-r--r--apps/web/public/icons/icon/bring-forward.svg3
-rw-r--r--apps/web/public/icons/icon/bring-to-front.svg3
-rw-r--r--apps/web/public/icons/icon/broken.svg5
-rw-r--r--apps/web/public/icons/icon/check-circle.svg4
-rw-r--r--apps/web/public/icons/icon/check.svg3
-rw-r--r--apps/web/public/icons/icon/chevron-down.svg3
-rw-r--r--apps/web/public/icons/icon/chevron-left.svg3
-rw-r--r--apps/web/public/icons/icon/chevron-right.svg3
-rw-r--r--apps/web/public/icons/icon/chevron-up.svg3
-rw-r--r--apps/web/public/icons/icon/chevrons-ne.svg3
-rw-r--r--apps/web/public/icons/icon/chevrons-sw.svg3
-rw-r--r--apps/web/public/icons/icon/clipboard-copied.svg4
-rw-r--r--apps/web/public/icons/icon/clipboard-copy.svg15
-rw-r--r--apps/web/public/icons/icon/color.svg3
-rw-r--r--apps/web/public/icons/icon/cross-2.svg3
-rw-r--r--apps/web/public/icons/icon/cross-circle.svg4
-rw-r--r--apps/web/public/icons/icon/dash-dashed.svg3
-rw-r--r--apps/web/public/icons/icon/dash-dotted.svg14
-rw-r--r--apps/web/public/icons/icon/dash-draw.svg3
-rw-r--r--apps/web/public/icons/icon/dash-solid.svg3
-rw-r--r--apps/web/public/icons/icon/disconnected.svg4
-rw-r--r--apps/web/public/icons/icon/discord.svg12
-rw-r--r--apps/web/public/icons/icon/distribute-horizontal.svg6
-rw-r--r--apps/web/public/icons/icon/distribute-vertical.svg6
-rw-r--r--apps/web/public/icons/icon/dot.svg3
-rw-r--r--apps/web/public/icons/icon/dots-horizontal.svg5
-rw-r--r--apps/web/public/icons/icon/dots-vertical.svg5
-rw-r--r--apps/web/public/icons/icon/drag-handle-dots.svg8
-rw-r--r--apps/web/public/icons/icon/duplicate.svg3
-rw-r--r--apps/web/public/icons/icon/edit.svg3
-rw-r--r--apps/web/public/icons/icon/external-link.svg3
-rw-r--r--apps/web/public/icons/icon/fill-none.svg3
-rw-r--r--apps/web/public/icons/icon/fill-pattern.svg4
-rw-r--r--apps/web/public/icons/icon/fill-semi.svg3
-rw-r--r--apps/web/public/icons/icon/fill-solid.svg4
-rw-r--r--apps/web/public/icons/icon/follow.svg4
-rw-r--r--apps/web/public/icons/icon/following.svg4
-rw-r--r--apps/web/public/icons/icon/font-draw.svg3
-rw-r--r--apps/web/public/icons/icon/font-mono.svg4
-rw-r--r--apps/web/public/icons/icon/font-sans.svg4
-rw-r--r--apps/web/public/icons/icon/font-serif.svg4
-rw-r--r--apps/web/public/icons/icon/geo-arrow-down.svg3
-rw-r--r--apps/web/public/icons/icon/geo-arrow-left.svg3
-rw-r--r--apps/web/public/icons/icon/geo-arrow-right.svg3
-rw-r--r--apps/web/public/icons/icon/geo-arrow-up.svg3
-rw-r--r--apps/web/public/icons/icon/geo-check-box.svg5
-rw-r--r--apps/web/public/icons/icon/geo-cloud.svg3
-rw-r--r--apps/web/public/icons/icon/geo-diamond.svg3
-rw-r--r--apps/web/public/icons/icon/geo-ellipse.svg3
-rw-r--r--apps/web/public/icons/icon/geo-heart.svg3
-rw-r--r--apps/web/public/icons/icon/geo-hexagon.svg3
-rw-r--r--apps/web/public/icons/icon/geo-octagon.svg3
-rw-r--r--apps/web/public/icons/icon/geo-oval.svg3
-rw-r--r--apps/web/public/icons/icon/geo-pentagon.svg3
-rw-r--r--apps/web/public/icons/icon/geo-rectangle.svg3
-rw-r--r--apps/web/public/icons/icon/geo-rhombus-2.svg3
-rw-r--r--apps/web/public/icons/icon/geo-rhombus.svg3
-rw-r--r--apps/web/public/icons/icon/geo-star.svg3
-rw-r--r--apps/web/public/icons/icon/geo-trapezoid.svg3
-rw-r--r--apps/web/public/icons/icon/geo-triangle.svg3
-rw-r--r--apps/web/public/icons/icon/geo-x-box.svg5
-rw-r--r--apps/web/public/icons/icon/github.svg3
-rw-r--r--apps/web/public/icons/icon/group.svg10
-rw-r--r--apps/web/public/icons/icon/horizontal-align-end.svg5
-rw-r--r--apps/web/public/icons/icon/horizontal-align-middle.svg7
-rw-r--r--apps/web/public/icons/icon/horizontal-align-start.svg5
-rw-r--r--apps/web/public/icons/icon/info-circle.svg5
-rw-r--r--apps/web/public/icons/icon/leading.svg4
-rw-r--r--apps/web/public/icons/icon/link.svg4
-rw-r--r--apps/web/public/icons/icon/lock.svg4
-rw-r--r--apps/web/public/icons/icon/menu.svg5
-rw-r--r--apps/web/public/icons/icon/minus.svg3
-rw-r--r--apps/web/public/icons/icon/mixed.svg11
-rw-r--r--apps/web/public/icons/icon/pack.svg3
-rw-r--r--apps/web/public/icons/icon/plus.svg3
-rw-r--r--apps/web/public/icons/icon/question-mark-circle.svg5
-rw-r--r--apps/web/public/icons/icon/question-mark.svg4
-rw-r--r--apps/web/public/icons/icon/redo.svg3
-rw-r--r--apps/web/public/icons/icon/reset-zoom.svg6
-rw-r--r--apps/web/public/icons/icon/rotate-ccw.svg4
-rw-r--r--apps/web/public/icons/icon/rotate-cw.svg4
-rw-r--r--apps/web/public/icons/icon/send-backward.svg3
-rw-r--r--apps/web/public/icons/icon/send-to-back.svg3
-rw-r--r--apps/web/public/icons/icon/share-1.svg4
-rw-r--r--apps/web/public/icons/icon/size-extra-large.svg4
-rw-r--r--apps/web/public/icons/icon/size-large.svg3
-rw-r--r--apps/web/public/icons/icon/size-medium.svg3
-rw-r--r--apps/web/public/icons/icon/size-small.svg3
-rw-r--r--apps/web/public/icons/icon/spline-cubic.svg3
-rw-r--r--apps/web/public/icons/icon/spline-line.svg3
-rw-r--r--apps/web/public/icons/icon/stack-horizontal.svg3
-rw-r--r--apps/web/public/icons/icon/stack-vertical.svg3
-rw-r--r--apps/web/public/icons/icon/stretch-horizontal.svg5
-rw-r--r--apps/web/public/icons/icon/stretch-vertical.svg4
-rw-r--r--apps/web/public/icons/icon/text-align-center.svg3
-rw-r--r--apps/web/public/icons/icon/text-align-left.svg3
-rw-r--r--apps/web/public/icons/icon/text-align-right.svg3
-rw-r--r--apps/web/public/icons/icon/toggle-off.svg4
-rw-r--r--apps/web/public/icons/icon/toggle-on.svg4
-rw-r--r--apps/web/public/icons/icon/tool-arrow.svg3
-rw-r--r--apps/web/public/icons/icon/tool-eraser.svg3
-rw-r--r--apps/web/public/icons/icon/tool-frame.svg3
-rw-r--r--apps/web/public/icons/icon/tool-hand.svg3
-rw-r--r--apps/web/public/icons/icon/tool-highlight.svg4
-rw-r--r--apps/web/public/icons/icon/tool-laser.svg6
-rw-r--r--apps/web/public/icons/icon/tool-line.svg3
-rw-r--r--apps/web/public/icons/icon/tool-media.svg4
-rw-r--r--apps/web/public/icons/icon/tool-note.svg4
-rw-r--r--apps/web/public/icons/icon/tool-pencil.svg4
-rw-r--r--apps/web/public/icons/icon/tool-pointer.svg3
-rw-r--r--apps/web/public/icons/icon/tool-screenshot.svg5
-rw-r--r--apps/web/public/icons/icon/tool-text.svg3
-rw-r--r--apps/web/public/icons/icon/trash.svg5
-rw-r--r--apps/web/public/icons/icon/twitter.svg4
-rw-r--r--apps/web/public/icons/icon/undo.svg3
-rw-r--r--apps/web/public/icons/icon/ungroup.svg9
-rw-r--r--apps/web/public/icons/icon/unlock.svg4
-rw-r--r--apps/web/public/icons/icon/vertical-align-end.svg5
-rw-r--r--apps/web/public/icons/icon/vertical-align-middle.svg7
-rw-r--r--apps/web/public/icons/icon/vertical-align-start.svg5
-rw-r--r--apps/web/public/icons/icon/warning-triangle.svg5
-rw-r--r--apps/web/public/icons/icon/zoom-in.svg6
-rw-r--r--apps/web/public/icons/icon/zoom-out.svg5
-rw-r--r--apps/web/public/landing-ui.svg4
-rw-r--r--apps/web/public/translations/ar.json295
-rw-r--r--apps/web/public/translations/ca.json295
-rw-r--r--apps/web/public/translations/cs.json352
-rw-r--r--apps/web/public/translations/da.json160
-rw-r--r--apps/web/public/translations/de.json310
-rw-r--r--apps/web/public/translations/en.json1
-rw-r--r--apps/web/public/translations/es.json299
-rw-r--r--apps/web/public/translations/fa.json343
-rw-r--r--apps/web/public/translations/fi.json312
-rw-r--r--apps/web/public/translations/fr.json372
-rw-r--r--apps/web/public/translations/gl.json349
-rw-r--r--apps/web/public/translations/he.json86
-rw-r--r--apps/web/public/translations/hi-in.json290
-rw-r--r--apps/web/public/translations/hr.json358
-rw-r--r--apps/web/public/translations/hu.json365
-rw-r--r--apps/web/public/translations/id.json373
-rw-r--r--apps/web/public/translations/it.json330
-rw-r--r--apps/web/public/translations/ja.json351
-rw-r--r--apps/web/public/translations/ko-kr.json372
-rw-r--r--apps/web/public/translations/ku.json93
-rw-r--r--apps/web/public/translations/languages.json150
-rw-r--r--apps/web/public/translations/main.json375
-rw-r--r--apps/web/public/translations/my.json112
-rw-r--r--apps/web/public/translations/ne.json295
-rw-r--r--apps/web/public/translations/no.json62
-rw-r--r--apps/web/public/translations/pl.json113
-rw-r--r--apps/web/public/translations/pt-br.json333
-rw-r--r--apps/web/public/translations/pt-pt.json86
-rw-r--r--apps/web/public/translations/ro.json371
-rw-r--r--apps/web/public/translations/ru.json347
-rw-r--r--apps/web/public/translations/sl.json373
-rw-r--r--apps/web/public/translations/sv.json96
-rw-r--r--apps/web/public/translations/te.json112
-rw-r--r--apps/web/public/translations/th.json295
-rw-r--r--apps/web/public/translations/tr.json355
-rw-r--r--apps/web/public/translations/uk.json347
-rw-r--r--apps/web/public/translations/vi.json295
-rw-r--r--apps/web/public/translations/zh-cn.json353
-rw-r--r--apps/web/public/translations/zh-tw.json316
-rw-r--r--apps/web/server/auth.ts (renamed from apps/web/app/helpers/server/auth.ts)25
-rw-r--r--apps/web/server/db/index.ts (renamed from apps/web/app/helpers/server/db/index.ts)0
-rw-r--r--apps/web/server/db/schema.ts (renamed from apps/web/app/helpers/server/db/schema.ts)105
-rw-r--r--package.json25
-rw-r--r--packages/shared-types/index.ts62
-rw-r--r--packages/tailwind-config/globals.css123
-rw-r--r--packages/tailwind-config/tailwind.config.ts6
-rw-r--r--packages/ui/components/icons.tsx4
-rw-r--r--packages/ui/icons/index.ts8
-rw-r--r--packages/ui/icons/nextarrow.svg3
-rw-r--r--packages/ui/icons/search.svg3
-rw-r--r--packages/ui/icons/url.svg4
-rw-r--r--packages/ui/shadcn/accordion.tsx54
-rw-r--r--packages/ui/shadcn/select.tsx160
-rw-r--r--packages/ui/shadcn/separator.tsx21
-rw-r--r--packages/ui/shadcn/sonner.tsx31
-rw-r--r--packages/ui/shadcn/tabs.tsx42
-rw-r--r--packages/ui/shadcn/textarea.tsx24
-rw-r--r--turbo.json2
314 files changed, 16482 insertions, 518 deletions
diff --git a/SETUP-GUIDE.md b/SETUP-GUIDE.md
index 7d69b545..f51b1cb1 100644
--- a/SETUP-GUIDE.md
+++ b/SETUP-GUIDE.md
@@ -13,12 +13,13 @@
3. Create a `.dev.vars` file in `apps/web` with the following content:
```bash
-GOOGLE_CLIENT_ID="-"
-GOOGLE_CLIENT_SECRET="-"
+GOOGLE_CLIENT_ID="-" // required, visit https://developers.google.com/identity/protocols/oauth2
+GOOGLE_CLIENT_SECRET="-" // required
NEXTAUTH_SECRET='nextauthsecret'
DATABASE_URL='database.sqlite'
NEXTAUTH_URL='http://localhost:3000'
BACKEND_SECURITY_KEY='veryrandomsecuritykey'
+BACKEND_BASE_URL="where your backend is hosted"
```
4. Setup the database:
@@ -28,10 +29,10 @@ First, edit the `wrangler.toml` file in `apps/web` to point the d1 database to y
You can create a d1 database by running this command
```
-wrangler d1 create DATABASE_NAME
+bunx wrangler d1 create <YOUR_DATABASE_NAME>
```
-And then replace these values
+And then replace database_name and database_id with the values
```
[[d1_databases]]
@@ -43,10 +44,12 @@ database_id = "YOUR_DB_ID"
Simply run this command in `apps/web`
```
-wrangler d1 execute dev-d1-anycontext --local --file=db/prepare.sql
+bunx wrangler d1 migrations apply <YOUR_DATABASE_NAME>
```
-If it runs, you can set up the cloud database as well by removing the `--local` flag.
+If it runs, you can set up the cloud database as well by removing the `--local` flag,
+
+if you just want to contribute to frontend then just run `bun run dev` in the root of the project and done! (you won't be able to try ai stuff), otherwise continue...
5. You need to host your own worker for the `apps/cf-ai-backend` module.
diff --git a/apps/browser-rendering b/apps/browser-rendering
-Subproject b37c962365a36cf342a31a196f4908f4f134355
+Subproject 4d21045a45fdbf56b7483d3704ae0474ebf044f
diff --git a/apps/cf-ai-backend/README.md b/apps/cf-ai-backend/README.md
index 86409f29..91d6b77e 100644
--- a/apps/cf-ai-backend/README.md
+++ b/apps/cf-ai-backend/README.md
@@ -1,58 +1,50 @@
-# Hono minimal project
+baseURL: https://new-cf-ai-backend.dhravya.workers.dev
-This is a minimal project with [Hono](https://github.com/honojs/hono/) for Cloudflare Workers.
+Authentication:
+You must authenticate with a header and `Authorization: bearer token` for each request in `/api/*` routes.
-## Features
+### Add content:
-- Minimal
-- TypeScript
-- Wrangler to develop and deploy.
-- [Jest](https://jestjs.io/ja/) for testing.
-
-## Usage
-
-Initialize
+POST `/api/add` with
```
-npx create-cloudflare my-app https://github.com/honojs/hono-minimal
+body {
+ pageContent: z.string(),
+ title: z.string().optional(),
+ description: z.string().optional(),
+ space: z.string().optional(),
+ url: z.string(),
+ user: z.string(),
+}
```
-Install
+### Query without user data
-```
-yarn install
-```
+GET `/api/ask` with
+query `?query=testing`
-Develop
+(this is temp but works perfectly, will change soon for chat use cases specifically)
-```
-yarn dev
-```
+### Query vectorize and get results in natural language
-Test
+POST `/api/chat` with
```
-yarn test
-```
-
-Deploy
+query paramters (?query=...&" {
+ query: z.string(),
+ topK: z.number().optional().default(10),
+ user: z.string(),
+ spaces: z.string().optional(),
+ sourcesOnly: z.string().optional().default("false"),
+ model: z.string().optional().default("gpt-4o"),
+ }
+body z.object({
+ chatHistory: z.array(contentObj).optional(),
+});
```
-yarn deploy
-```
-
-## Examples
-
-See: <https://github.com/honojs/examples>
-
-## For more information
-
-See: <https://honojs.dev>
-
-## Author
-
-Yusuke Wada <https://github.com/yusukebe>
-## License
+### Delete vectors
-MIT
+DELETE `/api/delete` with
+query param websiteUrl, user
diff --git a/apps/cf-ai-backend/src/helper.ts b/apps/cf-ai-backend/src/helper.ts
index 87495c59..cef781be 100644
--- a/apps/cf-ai-backend/src/helper.ts
+++ b/apps/cf-ai-backend/src/helper.ts
@@ -21,8 +21,6 @@ export async function initQuery(
index: c.env.VECTORIZE_INDEX,
});
- const DEFAULT_MODEL = "gpt-4o";
-
let selectedModel:
| ReturnType<ReturnType<typeof createOpenAI>>
| ReturnType<ReturnType<typeof createGoogleGenerativeAI>>
@@ -52,12 +50,6 @@ export async function initQuery(
break;
}
- if (!selectedModel) {
- throw new Error(
- `Model ${model} not found and default model ${DEFAULT_MODEL} is also not available.`,
- );
- }
-
return { store, model: selectedModel };
}
@@ -72,19 +64,46 @@ export async function deleteDocument({
c: Context<{ Bindings: Env }>;
store: CloudflareVectorizeStore;
}) {
- const toBeDeleted = `${url}-${user}`;
+ const toBeDeleted = `${url}#supermemory-web`;
const random = seededRandom(toBeDeleted);
const uuid =
random().toString(36).substring(2, 15) +
random().toString(36).substring(2, 15);
- await c.env.KV.list({ prefix: uuid }).then(async (keys) => {
- for (const key of keys.keys) {
- await c.env.KV.delete(key.name);
- await store.delete({ ids: [key.name] });
+ const allIds = await c.env.KV.list({ prefix: uuid });
+
+ if (allIds.keys.length > 0) {
+ const savedVectorIds = allIds.keys.map((key) => key.name);
+ const vectors = await c.env.VECTORIZE_INDEX.getByIds(savedVectorIds);
+ // We don't actually delete document directly, we just remove the user from the metadata.
+ // If there's no user left, we can delete the document.
+ const newVectors = vectors.map((vector) => {
+ delete vector.metadata[`user-${user}`];
+
+ // Get count of how many users are left
+ const userCount = Object.keys(vector.metadata).filter((key) =>
+ key.startsWith("user-"),
+ ).length;
+
+ // If there's no user left, we can delete the document.
+ // need to make sure that every chunk is deleted otherwise it would be problematic.
+ if (userCount === 0) {
+ store.delete({ ids: savedVectorIds });
+ void Promise.all(savedVectorIds.map((id) => c.env.KV.delete(id)));
+ return null;
+ }
+
+ return vector;
+ });
+
+ // If all vectors are null (deleted), we can delete the KV too. Otherwise, we update (upsert) the vectors.
+ if (newVectors.every((v) => v === null)) {
+ await c.env.KV.delete(uuid);
+ } else {
+ await c.env.VECTORIZE_INDEX.upsert(newVectors.filter((v) => v !== null));
}
- });
+ }
}
export async function batchCreateChunksAndEmbeddings({
@@ -98,19 +117,47 @@ export async function batchCreateChunksAndEmbeddings({
chunks: string[];
context: Context<{ Bindings: Env }>;
}) {
- const ourID = `${body.url}-${body.user}`;
+ //! NOTE that we use #supermemory-web to ensure that
+ //! If a user saves it through the extension, we don't want other users to be able to see it.
+ // Requests from the extension should ALWAYS have a unique ID with the USERiD in it.
+ // I cannot stress this enough, important for security.
+ const ourID = `${body.url}#supermemory-web`;
+ const random = seededRandom(ourID);
+ const uuid =
+ random().toString(36).substring(2, 15) +
+ random().toString(36).substring(2, 15);
- await deleteDocument({ url: body.url, user: body.user, c: context, store });
+ const allIds = await context.env.KV.list({ prefix: uuid });
- const random = seededRandom(ourID);
+ // If some chunks for that content already exist, we'll just update the metadata to include
+ // the user.
+ if (allIds.keys.length > 0) {
+ const savedVectorIds = allIds.keys.map((key) => key.name);
+ const vectors = await context.env.VECTORIZE_INDEX.getByIds(savedVectorIds);
+
+ // Now, we'll update all vector metadatas with one more userId and all spaceIds
+ const newVectors = vectors.map((vector) => {
+ vector.metadata = {
+ ...vector.metadata,
+ [`user-${body.user}`]: 1,
+
+ // For each space in body, add the spaceId to the vector metadata
+ ...(body.spaces ?? [])?.reduce((acc, space) => {
+ acc[`space-${body.user}-${space}`] = 1;
+ return acc;
+ }, {}),
+ };
+
+ return vector;
+ });
+
+ await context.env.VECTORIZE_INDEX.upsert(newVectors);
+ return;
+ }
for (let i = 0; i < chunks.length; i++) {
const chunk = chunks[i];
- const uuid =
- random().toString(36).substring(2, 15) +
- random().toString(36).substring(2, 15) +
- "-" +
- i;
+ const chunkId = `${uuid}-${i}`;
const newPageContent = `Title: ${body.title}\nDescription: ${body.description}\nURL: ${body.url}\nContent: ${chunk}`;
@@ -121,19 +168,25 @@ export async function batchCreateChunksAndEmbeddings({
metadata: {
title: body.title?.slice(0, 50) ?? "",
description: body.description ?? "",
- space: body.space ?? "",
url: body.url,
- user: body.user,
+ type: body.type ?? "page",
+ content: newPageContent,
+
+ [`user-${body.user}`]: 1,
+ ...body.spaces?.reduce((acc, space) => {
+ acc[`space-${body.user}-${space}`] = 1;
+ return acc;
+ }, {}),
},
},
],
{
- ids: [uuid],
+ ids: [chunkId],
},
);
console.log("Docs added: ", docs);
- await context.env.KV.put(uuid, ourID);
+ await context.env.KV.put(chunkId, ourID);
}
}
diff --git a/apps/cf-ai-backend/src/index.test.ts b/apps/cf-ai-backend/src/index.test.ts
deleted file mode 100644
index bbf66fb5..00000000
--- a/apps/cf-ai-backend/src/index.test.ts
+++ /dev/null
@@ -1,13 +0,0 @@
-import app from ".";
-
-// TODO: write more tests
-describe("Test the application", () => {
- it("Should return 200 response", async () => {
- const res = await app.request("http://localhost/");
- expect(res.status).toBe(200);
- }),
- it("Should return 404 response", async () => {
- const res = await app.request("http://localhost/404");
- expect(res.status).toBe(404);
- });
-});
diff --git a/apps/cf-ai-backend/src/index.ts b/apps/cf-ai-backend/src/index.ts
index 19770dec..effdf517 100644
--- a/apps/cf-ai-backend/src/index.ts
+++ b/apps/cf-ai-backend/src/index.ts
@@ -1,6 +1,6 @@
import { z } from "zod";
import { Hono } from "hono";
-import { CoreMessage, streamText } from "ai";
+import { CoreMessage, generateText, streamText } from "ai";
import { chatObj, Env, vectorObj } from "./types";
import {
batchCreateChunksAndEmbeddings,
@@ -14,9 +14,17 @@ import { bearerAuth } from "hono/bearer-auth";
import { zValidator } from "@hono/zod-validator";
import chunkText from "./utils/chonker";
import { systemPrompt, template } from "./prompts/prompt1";
+import { swaggerUI } from "@hono/swagger-ui";
const app = new Hono<{ Bindings: Env }>();
+app.get(
+ "/ui",
+ swaggerUI({
+ url: "/doc",
+ }),
+);
+
// ------- MIDDLEWARES -------
app.use("*", poweredBy());
app.use("*", timing());
@@ -31,6 +39,17 @@ app.use("/api/", async (c, next) => {
});
// ------- MIDDLEWARES END -------
+const fileSchema = z
+ .instanceof(File)
+ .refine(
+ (file) => file.size <= 10 * 1024 * 1024,
+ "File size should be less than 10MB",
+ ) // Validate file size
+ .refine(
+ (file) => ["image/jpeg", "image/png", "image/gif"].includes(file.type),
+ "Invalid file type",
+ ); // Validate file type
+
app.get("/", (c) => {
return c.text("Supermemory backend API is running!");
});
@@ -54,6 +73,82 @@ app.post("/api/add", zValidator("json", vectorObj), async (c) => {
return c.json({ status: "ok" });
});
+app.post(
+ "/api/add-with-image",
+ zValidator(
+ "form",
+ z.object({
+ images: z
+ .array(fileSchema)
+ .min(1, "At least one image is required")
+ .optional(),
+ "images[]": z
+ .array(fileSchema)
+ .min(1, "At least one image is required")
+ .optional(),
+ text: z.string().optional(),
+ spaces: z.array(z.string()).optional(),
+ url: z.string(),
+ user: z.string(),
+ }),
+ (c) => {
+ console.log(c);
+ },
+ ),
+ async (c) => {
+ const body = c.req.valid("form");
+
+ const { store } = await initQuery(c);
+
+ if (!(body.images || body["images[]"])) {
+ return c.json({ status: "error", message: "No images found" }, 400);
+ }
+
+ const imagePromises = (body.images ?? body["images[]"]).map(
+ async (image) => {
+ const buffer = await image.arrayBuffer();
+ const input = {
+ image: [...new Uint8Array(buffer)],
+ prompt:
+ "What's in this image? caption everything you see in great detail. If it has text, do an OCR and extract all of it.",
+ max_tokens: 1024,
+ };
+ const response = await c.env.AI.run(
+ "@cf/llava-hf/llava-1.5-7b-hf",
+ input,
+ );
+ console.log(response.description);
+ return response.description;
+ },
+ );
+
+ const imageDescriptions = await Promise.all(imagePromises);
+
+ await batchCreateChunksAndEmbeddings({
+ store,
+ body: {
+ url: body.url,
+ user: body.user,
+ type: "image",
+ description:
+ imageDescriptions.length > 1
+ ? `A group of ${imageDescriptions.length} images on ${body.url}`
+ : imageDescriptions[0],
+ spaces: body.spaces,
+ pageContent: imageDescriptions.join("\n"),
+ title: "Image content from the web",
+ },
+ chunks: [
+ imageDescriptions,
+ ...(body.text ? chunkText(body.text, 1536) : []),
+ ].flat(),
+ context: c,
+ });
+
+ return c.json({ status: "ok" });
+ },
+);
+
app.get(
"/api/ask",
zValidator(
@@ -85,8 +180,8 @@ app.post(
"query",
z.object({
query: z.string(),
- topK: z.number().optional().default(10),
user: z.string(),
+ topK: z.number().optional().default(10),
spaces: z.string().optional(),
sourcesOnly: z.string().optional().default("false"),
model: z.string().optional().default("gpt-4o"),
@@ -97,30 +192,29 @@ app.post(
const query = c.req.valid("query");
const body = c.req.valid("json");
- if (body.chatHistory) {
- body.chatHistory = body.chatHistory.map((i) => ({
- ...i,
- content: i.parts.length > 0 ? i.parts.join(" ") : i.content,
- }));
- }
-
const sourcesOnly = query.sourcesOnly === "true";
- const spaces = query.spaces?.split(",") || [undefined];
+ const spaces = query.spaces?.split(",") ?? [undefined];
// Get the AI model maker and vector store
const { model, store } = await initQuery(c, query.model);
- const filter: VectorizeVectorMetadataFilter = { user: query.user };
+ const filter: VectorizeVectorMetadataFilter = {
+ [`user-${query.user}`]: 1,
+ };
+ console.log("Spaces", spaces);
// Converting the query to a vector so that we can search for similar vectors
const queryAsVector = await store.embeddings.embedQuery(query.query);
const responses: VectorizeMatches = { matches: [], count: 0 };
+ console.log("hello world", spaces);
+
// SLICED to 5 to avoid too many queries
for (const space of spaces.slice(0, 5)) {
- if (space !== undefined) {
+ console.log("space", space);
+ if (!space && spaces.length > 1) {
// it's possible for space list to be [undefined] so we only add space filter conditionally
- filter.space = space;
+ filter[`space-${query.user}-${space}`] = 1;
}
// Because there's no OR operator in the filter, we have to make multiple queries
@@ -173,29 +267,20 @@ app.post(
dataPoint.id.toString(),
);
- // We are getting the content ID back, so that the frontend can show the actual sources properly.
- // it IS a lot of DB calls, i completely agree.
- // TODO: return metadata value here, so that the frontend doesn't have to re-fetch anything.
const storedContent = await Promise.all(
idsAsStrings.map(async (id) => await c.env.KV.get(id)),
);
- return c.json({ ids: storedContent });
- }
-
- const vec = responses.matches.map((data) => ({ metadata: data.metadata }));
+ const metadata = normalizedData.map((datapoint) => datapoint.metadata);
- const vecWithScores = vec.map((v, i) => ({
- ...v,
- score: sortedHighScoreData[i].score,
- normalisedScore: sortedHighScoreData[i].normalizedScore,
- }));
+ return c.json({ ids: storedContent, metadata });
+ }
- const preparedContext = vecWithScores.map(
- ({ metadata, score, normalisedScore }) => ({
+ const preparedContext = normalizedData.map(
+ ({ metadata, score, normalizedScore }) => ({
context: `Website title: ${metadata!.title}\nDescription: ${metadata!.description}\nURL: ${metadata!.url}\nContent: ${metadata!.text}`,
score,
- normalisedScore,
+ normalizedScore,
}),
);
@@ -245,4 +330,20 @@ app.delete(
},
);
+app.get('/api/editorai', zValidator(
+ "query",
+ z.object({
+ context: z.string(),
+ request: z.string(),
+ }),
+), async (c)=> {
+ const { context, request } = c.req.valid("query");
+
+ const { model } = await initQuery(c);
+
+ const {text} = await generateText({ model, prompt: `${request}-${context}`, maxTokens: 224 });
+
+ return c.json({completion: text});
+})
+
export default app;
diff --git a/apps/cf-ai-backend/src/prompts/prompt1.ts b/apps/cf-ai-backend/src/prompts/prompt1.ts
index aa7694d3..289495b6 100644
--- a/apps/cf-ai-backend/src/prompts/prompt1.ts
+++ b/apps/cf-ai-backend/src/prompts/prompt1.ts
@@ -6,28 +6,24 @@ To generate your answer:
- Carefully analyze the question and identify the key information needed to address it
- Locate the specific parts of each context that contain this key information
- Compare the relevance scores of the provided contexts
-- In the <justification> tags, provide a brief justification for which context(s) are more relevant to answering the question based on the scores
- Concisely summarize the relevant information from the higher-scoring context(s) in your own words
- Provide a direct answer to the question
- Use markdown formatting in your answer, including bold, italics, and bullet points as appropriate to improve readability and highlight key points
- Give detailed and accurate responses for things like 'write a blog' or long-form questions.
- The normalisedScore is a value in which the scores are 'balanced' to give a better representation of the relevance of the context, between 1 and 100, out of the top 10 results
-
-Provide your justification between <justification> tags and your final answer between <answer> tags, formatting both in markdown.
-
+- provide your justification in the end, in a <justification> </justification> tag
If no context is provided, introduce yourself and explain that the user can save content which will allow you to answer questions about that content in the future. Do not provide an answer if no context is provided.`;
export const template = ({ contexts, question }) => {
// Map over contexts to generate the context and score parts
const contextParts = contexts
.map(
- ({ context, score, normalisedScore }) => `
+ ({ context, normalisedScore }) => `
<context>
${context}
</context>
<context_score>
- score: ${score}
normalisedScore: ${normalisedScore}
</context_score>`,
)
diff --git a/apps/cf-ai-backend/src/types.ts b/apps/cf-ai-backend/src/types.ts
index bea4bf80..417d6320 100644
--- a/apps/cf-ai-backend/src/types.ts
+++ b/apps/cf-ai-backend/src/types.ts
@@ -2,7 +2,7 @@ import { z } from "zod";
export type Env = {
VECTORIZE_INDEX: VectorizeIndex;
- AI: Fetcher;
+ AI: Ai;
SECURITY_KEY: string;
OPENAI_API_KEY: string;
GOOGLE_AI_API_KEY: string;
@@ -43,7 +43,8 @@ export const vectorObj = z.object({
pageContent: z.string(),
title: z.string().optional(),
description: z.string().optional(),
- space: z.string().optional(),
+ spaces: z.array(z.string()).optional(),
url: z.string(),
user: z.string(),
+ type: z.string().optional().default("page"),
});
diff --git a/apps/cf-ai-backend/src/utils/OpenAIEmbedder.ts b/apps/cf-ai-backend/src/utils/OpenAIEmbedder.ts
index 3514f579..be5839b1 100644
--- a/apps/cf-ai-backend/src/utils/OpenAIEmbedder.ts
+++ b/apps/cf-ai-backend/src/utils/OpenAIEmbedder.ts
@@ -1,3 +1,5 @@
+import { z } from "zod";
+
interface OpenAIEmbeddingsParams {
apiKey: string;
modelName: string;
@@ -32,12 +34,22 @@ export class OpenAIEmbeddings {
}),
});
- const data = (await response.json()) as {
- data: {
- embedding: number[];
- }[];
- };
+ const data = await response.json();
+
+ const zodTypeExpected = z.object({
+ data: z.array(
+ z.object({
+ embedding: z.array(z.number()),
+ }),
+ ),
+ });
+
+ const json = zodTypeExpected.safeParse(data);
+
+ if (!json.success) {
+ throw new Error("Invalid response from OpenAI: " + json.error.message);
+ }
- return data.data[0].embedding;
+ return json.data.data[0].embedding;
}
}
diff --git a/apps/cf-ai-backend/src/utils/chonker.ts b/apps/cf-ai-backend/src/utils/chonker.ts
index 39d4b458..c63020be 100644
--- a/apps/cf-ai-backend/src/utils/chonker.ts
+++ b/apps/cf-ai-backend/src/utils/chonker.ts
@@ -1,5 +1,8 @@
import nlp from "compromise";
+/**
+ * Split text into chunks of specified max size with some overlap for continuity.
+ */
export default function chunkText(
text: string,
maxChunkSize: number,
diff --git a/apps/cf-ai-backend/src/utils/seededRandom.ts b/apps/cf-ai-backend/src/utils/seededRandom.ts
index 36a1e4f9..9e315ee8 100644
--- a/apps/cf-ai-backend/src/utils/seededRandom.ts
+++ b/apps/cf-ai-backend/src/utils/seededRandom.ts
@@ -1,5 +1,9 @@
import { MersenneTwister19937, integer } from "random-js";
+/**
+ * Hashes a string to a 32-bit integer.
+ * @param {string} seed - The input string to hash.
+ */
function hashString(seed: string) {
let hash = 0;
for (let i = 0; i < seed.length; i++) {
@@ -10,6 +14,9 @@ function hashString(seed: string) {
return hash;
}
+/**
+ * returns a funtion that generates same sequence of random numbers for a given seed between 0 and 1.
+ */
export function seededRandom(seed: string) {
const seedHash = hashString(seed);
const engine = MersenneTwister19937.seed(seedHash);
diff --git a/apps/cf-ai-backend/tsconfig.json b/apps/cf-ai-backend/tsconfig.json
index 2b75d5a0..fcdf6914 100644
--- a/apps/cf-ai-backend/tsconfig.json
+++ b/apps/cf-ai-backend/tsconfig.json
@@ -1,6 +1,7 @@
{
"compilerOptions": {
"lib": ["ES2020"],
- "types": ["@cloudflare/workers-types"]
+ "types": ["@cloudflare/workers-types"],
+ "downlevelIteration": true
}
}
diff --git a/apps/cf-ai-backend/wrangler.toml b/apps/cf-ai-backend/wrangler.toml
index db0ae945..fa883195 100644
--- a/apps/cf-ai-backend/wrangler.toml
+++ b/apps/cf-ai-backend/wrangler.toml
@@ -5,7 +5,7 @@ node_compat = true
[[vectorize]]
binding = "VECTORIZE_INDEX"
-index_name = "supermem-vector"
+index_name = "supermem-vector-dev"
[ai]
binding = "AI"
diff --git a/apps/web/app/(auth)/auth-buttons.tsx b/apps/web/app/(auth)/auth-buttons.tsx
index 0e99213e..5b0ad06e 100644
--- a/apps/web/app/(auth)/auth-buttons.tsx
+++ b/apps/web/app/(auth)/auth-buttons.tsx
@@ -2,7 +2,7 @@
import { Button } from "@repo/ui/shadcn/button";
import React from "react";
-import { signIn } from "../helpers/server/auth";
+import { signIn } from "../../server/auth";
function SignIn() {
return (
diff --git a/apps/web/app/(auth)/signin/page.tsx b/apps/web/app/(auth)/signin/page.tsx
index 2f913a75..d7bad8da 100644
--- a/apps/web/app/(auth)/signin/page.tsx
+++ b/apps/web/app/(auth)/signin/page.tsx
@@ -1,7 +1,7 @@
import Image from "next/image";
import Link from "next/link";
import Logo from "@/public/logo.svg";
-import { signIn } from "@/app/helpers/server/auth";
+import { signIn } from "@/server/auth";
import { Google } from "@repo/ui/components/icons";
export const runtime = "edge";
@@ -9,26 +9,22 @@ export const runtime = "edge";
async function Signin() {
return (
<div className="flex items-center justify-between min-h-screen">
- <div className="relative w-1/2 flex items-center min-h-screen bg-secondary p-8">
+ <div className="relative w-full lg:w-1/2 flex items-center justify-center lg:justify-start min-h-screen bg-secondary p-8">
<div className="absolute top-0 left-0 p-8 text-white inline-flex gap-2 items-center">
- <Image
- src={Logo}
- alt="SuperMemory logo"
- className="hover:brightness-125 duration-200"
- />
+ <Image src={Logo} alt="SuperMemory logo" className="brightness-100" />
<span className="text-xl">SuperMemory.ai</span>
</div>
<div>
<h1 className="text-5xl text-foreground mb-8">
- Hello, <span className="text-white">human</span>{" "}
+ Hello, <span className="text-white">human</span>
</h1>
<p className="text-white mb-8 text-lg">
Write, ideate, and learn with all the wisdom of your bookmarks.
</p>
<div className="flex items-center gap-4">
<div
- className={`transition-width z-20 rounded-2xl bg-gradient-to-br from-gray-200/70 to-transparent p-[0.7px] duration-300 ease-in-out w-3/4`}
+ className={`transition-width z-20 rounded-2xl bg-gradient-to-br from-gray-200/70 to-transparent p-[0.7px] duration-300 ease-in-out w-3/4 hover:bg-opacity-90 hover:shadow-lg group`}
>
<form
action={async () => {
@@ -40,7 +36,7 @@ async function Signin() {
>
<button
type="submit"
- className={`relative text-white transition-width flex justify-between w-full items-center rounded-2xl bg-[#37485E] px-6 py-4 outline-none duration-300 focus:outline-none`}
+ className={`relative text-white transition-width flex justify-between w-full items-center rounded-2xl bg-[#37485E] px-6 py-4 outline-none duration-300 focus:outline-none group-hover:bg-opacity-50 group`}
>
<Google />
<span className="relative w-full self-start">
@@ -50,21 +46,19 @@ async function Signin() {
</form>
</div>
</div>
- <div className="text-slate-500 mt-16">
- By continuing, you agree to the
- <Link href="/terms" className="text-slate-200">
- {" "}
+ <div className="text-slate-100 absolute bottom-4 lg:left-8 lg:-translate-x-0 left-1/2 -translate-x-1/2 ">
+ <Link href="/terms" className="text-slate-300">
Terms of Service
</Link>{" "}
- and
- <Link href="/privacy" className="text-slate-200">
+ |
+ <Link href="/privacy" className="text-slate-300">
{" "}
Privacy Policy
</Link>
</div>
</div>
</div>
- <div className="w-1/2 flex flex-col items-center justify-center min-h-screen">
+ <div className="w-1/2 hidden lg:flex flex-col items-center justify-center min-h-screen">
<span className="text-3xl leading-relaxed italic mb-8">
Ready for your{" "}
<span className="text-white font-bold">Second brain</span>?
diff --git a/apps/web/app/(canvas)/canvas.tsx b/apps/web/app/(canvas)/canvas.tsx
new file mode 100644
index 00000000..9ec57d6d
--- /dev/null
+++ b/apps/web/app/(canvas)/canvas.tsx
@@ -0,0 +1,55 @@
+import { useCallback, useEffect, useMemo, useState } from "react";
+import { Editor, Tldraw, setUserPreferences, TLStoreWithStatus } from "tldraw";
+import { createAssetFromUrl } from "./lib/createAssetUrl";
+import "tldraw/tldraw.css";
+import { components } from "./enabledComp";
+import { twitterCardUtil } from "./twitterCard";
+import createEmbedsFromUrl from "./lib/createEmbeds";
+import { loadRemoteSnapshot } from "./lib/loadSnap";
+import { SaveStatus } from "./savesnap";
+import { getAssetUrls } from '@tldraw/assets/selfHosted'
+import { memo } from 'react';
+
+export const Canvas = memo(()=>{
+ const [storeWithStatus, setStoreWithStatus] = useState<TLStoreWithStatus>({
+ status: "loading",
+ });
+ useEffect(() => {
+ const fetchStore = async () => {
+ const store = await loadRemoteSnapshot();
+
+ setStoreWithStatus({
+ store: store,
+ status: "not-synced",
+ });
+ };
+
+ fetchStore();
+ }, []);
+
+ const handleMount = useCallback((editor: Editor) => {
+ (window as any).app = editor;
+ (window as any).editor = editor;
+ editor.registerExternalAssetHandler("url", createAssetFromUrl);
+ editor.registerExternalContentHandler("url", ({ url, point, sources }) => {
+ createEmbedsFromUrl({ url, point, sources, editor });
+ });
+ }, []);
+
+ setUserPreferences({ id: "supermemory", isDarkMode: true });
+
+ const assetUrls = getAssetUrls()
+ return (
+ <Tldraw
+ assetUrls={assetUrls}
+ components={components}
+ store={storeWithStatus}
+ shapeUtils={[twitterCardUtil]}
+ onMount={handleMount}
+ >
+ <div className="absolute left-1/2 top-0 z-[1000000] flex -translate-x-1/2 gap-2 bg-[#2C3439] text-[#B3BCC5]">
+ <SaveStatus />
+ </div>
+ </Tldraw>
+ );
+})
diff --git a/apps/web/app/(canvas)/canvas/layout.tsx b/apps/web/app/(canvas)/canvas/layout.tsx
new file mode 100644
index 00000000..9bc3b6d7
--- /dev/null
+++ b/apps/web/app/(canvas)/canvas/layout.tsx
@@ -0,0 +1,13 @@
+import "../canvasStyles.css";
+
+export default function RootLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+ <div lang="en" className="bg-[#151515]">
+ <div>{children}</div>
+ </div>
+ );
+}
diff --git a/apps/web/app/(canvas)/canvas/page.tsx b/apps/web/app/(canvas)/canvas/page.tsx
new file mode 100644
index 00000000..366a4481
--- /dev/null
+++ b/apps/web/app/(canvas)/canvas/page.tsx
@@ -0,0 +1,99 @@
+"use client";
+
+// import Canvas from "./_components/canvas";
+import {Canvas} from "../canvas";
+import React, { useState } from "react";
+// import ReactTextareaAutosize from "react-textarea-autosize";
+import { Panel, PanelGroup, PanelResizeHandle } from "react-resizable-panels";
+import {
+ DragSvg,
+ SettingsSvg,
+ LinkSvg,
+ ThreeDBlock,
+ TextLoadingSvg,
+} from "../svg";
+
+function page() {
+ const [value, setValue] = useState("");
+ const [fullScreen, setFullScreen] = useState(false);
+
+ return (
+ <div className={`h-screen w-full ${ !fullScreen ? "px-4 py-6": "bg-[#1F2428]"} transition-all`}>
+ <div>
+ <PanelGroup className={` ${fullScreen ? "w-[calc(100vw-2rem)]" : "w-screen"} transition-all`} direction="horizontal">
+ <Panel onExpand={()=> {setTimeout(()=> setFullScreen(false), 50)}} onCollapse={()=> {setTimeout(()=> setFullScreen(true), 50)}} defaultSize={30} collapsible={true} minSize={22}>
+ <div className={`flex transition-all rounded-2xl ${fullScreen ? "h-screen": "h-[calc(100vh-3rem)]"} w-full flex-col overflow-hidden bg-[#1F2428]`}>
+ <div className="flex items-center justify-between bg-[#2C3439] px-4 py-2 text-lg font-medium text-[#989EA4]">
+ Change Filters
+ <SettingsSvg />
+ </div>
+ <div className="px-3 py-5">
+ <input
+ placeholder="search..."
+ onChange={(e) => {
+ setValue(e.target.value);
+ }}
+ value={value}
+ // rows={1}
+ className="w-full resize-none rounded-xl bg-[#151515] px-3 py-4 text-xl text-[#989EA4] outline-none focus:outline-none sm:max-h-52"
+ />
+ </div>
+ <div className="flex flex-col gap-10">
+ <div className="flex gap-4 px-3 text-[#989EA4]">
+ <TextLoadingSvg />
+ <h1>
+ Nvidia will most likely create monopoly in software industry
+ as they are already largest player in GPU hardware by 20...
+ </h1>
+ </div>
+ <div className="flex gap-4 px-3 text-[#989EA4]">
+ <ThreeDBlock />
+ <div className="flex flex-col gap-2">
+ <div>
+ <h1 className="line-clamp-3">
+ Nvidia currently dominates the GPU hardware market, with
+ a market share over 97%. This has led some to argue...
+ </h1>
+ </div>
+ <p className="line-clamp-1 text-[#369DFD]">
+ From space: GPU GOATS
+ </p>
+ </div>
+ </div>
+ <div className="flex gap-4 px-3 text-[#989EA4]">
+ <LinkSvg />
+ <div className="flex flex-col gap-2">
+ <div>
+ <h1 className="line-clamp-3">
+ Nvidia currently dominates the GPU hardware market, with
+ a market share over 97%. This has led some to argue...
+ </h1>
+ </div>
+ <p className="line-clamp-1 text-[#369DFD]">
+ Page url:
+ https://www.cnbc.com/2024/05/23/nvidia-keeps-hitting-records-can-investors-still-buy-the-stock.html?&qsearchterm=nvidia
+ </p>
+ </div>
+ </div>
+ </div>
+ </div>
+ </Panel>
+ <PanelResizeHandle className={`relative flex items-center transition-all justify-center ${!fullScreen && "px-1"}`}>
+ {/* <div className="absolute z-[1000000] top-1/2 -translate-y-1/2"> */}
+ <div className={`rounded-lg bg-[#2F363B] ${!fullScreen && "px-1"} transition-all py-2`}>
+ <DragSvg />
+ </div>
+ {/* </div> */}
+ </PanelResizeHandle>
+ <Panel className="relative" defaultSize={70} minSize={60}>
+ <div className={`absolute overflow-hidden transition-all inset-0 ${ fullScreen ? "h-screen " : "h-[calc(100vh-3rem)] rounded-2xl"} w-full`}>
+ <Canvas />
+ </div>
+ </Panel>
+ </PanelGroup>
+ </div>
+ </div>
+ );
+}
+
+export default page;
diff --git a/apps/web/app/(canvas)/canvasStyles.css b/apps/web/app/(canvas)/canvasStyles.css
new file mode 100644
index 00000000..a53d8c96
--- /dev/null
+++ b/apps/web/app/(canvas)/canvasStyles.css
@@ -0,0 +1,24 @@
+.tl-background {
+ background: #1F2428 !important;
+}
+
+.tlui-style-panel.tlui-style-panel__wrapper, .tlui-navigation-panel::before ,.tlui-menu-zone, .tlui-toolbar__tools, .tlui-popover__content, .tlui-menu, .tlui-button__help, .tlui-help-menu, .tlui-dialog__content {
+ background: #2C3439 !important;
+ border-top: #2C3439 !important;
+ border-right: #2C3439 !important;
+ border-bottom: #2C3439 !important;
+ border-left: #2C3439 !important;
+}
+
+.tlui-navigation-panel::before {
+ border-top: #2C3439 !important;
+ border-right: #2C3439 !important;
+}
+
+.tlui-minimap {
+ background: #2C3439 !important;
+}
+
+.tlui-minimap__canvas {
+ background: #1F2428 !important;
+} \ No newline at end of file
diff --git a/apps/web/app/(canvas)/enabledComp.tsx b/apps/web/app/(canvas)/enabledComp.tsx
new file mode 100644
index 00000000..5dbe6ee7
--- /dev/null
+++ b/apps/web/app/(canvas)/enabledComp.tsx
@@ -0,0 +1,22 @@
+import { TLUiComponents } from "tldraw";
+
+export const components: Partial<TLUiComponents> = {
+ ActionsMenu: null,
+ MainMenu: null,
+ QuickActions: null,
+ TopPanel: null,
+ DebugPanel: null,
+ DebugMenu: null,
+ // Minimap: null,
+ // ContextMenu: null,
+ // HelpMenu: null,
+ // ZoomMenu: null,
+ // StylePanel: null,
+ // PageMenu: null,
+ // NavigationPanel: null,
+ // Toolbar: null,
+ // KeyboardShortcutsDialog: null,
+ // HelperButtons: null,
+ // SharePanel: null,
+ // MenuPanel: null,
+}; \ No newline at end of file
diff --git a/apps/web/app/(canvas)/lib/createAssetUrl.ts b/apps/web/app/(canvas)/lib/createAssetUrl.ts
new file mode 100644
index 00000000..05c2baea
--- /dev/null
+++ b/apps/web/app/(canvas)/lib/createAssetUrl.ts
@@ -0,0 +1,94 @@
+import {
+ AssetRecordType,
+ TLAsset,
+ getHashForString,
+ truncateStringWithEllipsis,
+} from "tldraw";
+// import { BOOKMARK_ENDPOINT } from './config'
+
+interface ResponseBody {
+ title?: string;
+ description?: string;
+ image?: string;
+}
+
+export async function createAssetFromUrl({
+ url,
+}: {
+ type: "url";
+ url: string;
+}): Promise<TLAsset> {
+ // try {
+ // // First, try to get the meta data from our endpoint
+ // const meta = (await (
+ // await fetch(BOOKMARK_ENDPOINT, {
+ // method: 'POST',
+ // headers: {
+ // 'Content-Type': 'application/json',
+ // },
+ // body: JSON.stringify({
+ // url,
+ // }),
+ // })
+ // ).json()) as ResponseBody
+
+ // return {
+ // id: AssetRecordType.createId(getHashForString(url)),
+ // typeName: 'asset',
+ // type: 'bookmark',
+ // props: {
+ // src: url,
+ // description: meta.description ?? '',
+ // image: meta.image ?? '',
+ // title: meta.title ?? truncateStringWithEllipsis(url, 32),
+ // },
+ // meta: {},
+ // }
+ // } catch (error) {
+ // Otherwise, fallback to fetching data from the url
+
+ let meta: { image: string; title: string; description: string };
+
+ try {
+ const resp = await fetch(url, { method: "GET", mode: "no-cors" });
+ const html = await resp.text();
+ const doc = new DOMParser().parseFromString(html, "text/html");
+ meta = {
+ image:
+ doc.head
+ .querySelector('meta[property="og:image"]')
+ ?.getAttribute("content") ?? "",
+ title:
+ doc.head
+ .querySelector('meta[property="og:title"]')
+ ?.getAttribute("content") ?? truncateStringWithEllipsis(url, 32),
+ description:
+ doc.head
+ .querySelector('meta[property="og:description"]')
+ ?.getAttribute("content") ?? "",
+ };
+ } catch (error) {
+ console.error(error);
+ meta = {
+ image: "",
+ title: truncateStringWithEllipsis(url, 32),
+ description: "",
+ };
+ }
+
+ // Create the bookmark asset from the meta
+ return {
+ id: AssetRecordType.createId(getHashForString(url)),
+ typeName: "asset",
+ type: "bookmark",
+ props: {
+ src: url,
+ image: meta.image,
+ title: meta.title,
+ description: meta.description,
+ favicon: meta.image,
+ },
+ meta: {},
+ };
+ // }
+}
diff --git a/apps/web/app/(canvas)/lib/createEmbeds.ts b/apps/web/app/(canvas)/lib/createEmbeds.ts
new file mode 100644
index 00000000..53d81533
--- /dev/null
+++ b/apps/web/app/(canvas)/lib/createEmbeds.ts
@@ -0,0 +1,142 @@
+import { AssetRecordType, Editor, TLAsset, TLAssetId, TLBookmarkShape, TLExternalContentSource, TLShapePartial, Vec, VecLike, createShapeId, getEmbedInfo, getHashForString } from "tldraw";
+
+export default async function createEmbedsFromUrl({url, point, sources, editor}: {
+ url: string
+ point: VecLike | undefined
+ sources: TLExternalContentSource[] | undefined
+ editor: Editor
+}){
+
+ const position =
+ point ??
+ (editor.inputs.shiftKey
+ ? editor.inputs.currentPagePoint
+ : editor.getViewportPageBounds().center);
+
+ if (url?.includes("x.com") || url?.includes("twitter.com")) {
+ return editor.createShape({
+ type: "Twittercard",
+ x: position.x - 250,
+ y: position.y - 150,
+ props: { url: url },
+ });
+
+ }
+
+ // try to paste as an embed first
+ const embedInfo = getEmbedInfo(url);
+
+ if (embedInfo) {
+ return editor.putExternalContent({
+ type: "embed",
+ url: embedInfo.url,
+ point,
+ embed: embedInfo.definition,
+ });
+ }
+
+ const assetId: TLAssetId = AssetRecordType.createId(
+ getHashForString(url),
+ );
+ const shape = createEmptyBookmarkShape(editor, url, position);
+
+ // Use an existing asset if we have one, or else else create a new one
+ let asset = editor.getAsset(assetId) as TLAsset;
+ let shouldAlsoCreateAsset = false;
+ if (!asset) {
+ shouldAlsoCreateAsset = true;
+ try {
+ const bookmarkAsset = await editor.getAssetForExternalContent({
+ type: "url",
+ url,
+ });
+ const fetchWebsite: {
+ title?: string;
+ image?: string;
+ description?: string;
+ } = await (await fetch(`/api/unfirlsite?website=${url}`, {
+ method: "POST"
+ })).json()
+ if (bookmarkAsset){
+ if (fetchWebsite.title) bookmarkAsset.props.title = fetchWebsite.title;
+ if (fetchWebsite.image) bookmarkAsset.props.image = fetchWebsite.image;
+ if (fetchWebsite.description) bookmarkAsset.props.description = fetchWebsite.description;
+ }
+ if (!bookmarkAsset) throw Error("Could not create an asset");
+ asset = bookmarkAsset;
+ } catch (e) {
+ console.log(e)
+ return;
+ }
+ }
+
+ editor.batch(() => {
+ if (shouldAlsoCreateAsset) {
+ editor.createAssets([asset]);
+ }
+
+ editor.updateShapes([
+ {
+ id: shape.id,
+ type: shape.type,
+ props: {
+ assetId: asset.id,
+ },
+ },
+ ]);
+ });
+}
+
+function centerSelectionAroundPoint(editor: Editor, position: VecLike) {
+ // Re-position shapes so that the center of the group is at the provided point
+ const viewportPageBounds = editor.getViewportPageBounds()
+ let selectionPageBounds = editor.getSelectionPageBounds()
+
+ if (selectionPageBounds) {
+ const offset = selectionPageBounds!.center.sub(position)
+
+ editor.updateShapes(
+ editor.getSelectedShapes().map((shape) => {
+ const localRotation = editor.getShapeParentTransform(shape).decompose().rotation
+ const localDelta = Vec.Rot(offset, -localRotation)
+ return {
+ id: shape.id,
+ type: shape.type,
+ x: shape.x! - localDelta.x,
+ y: shape.y! - localDelta.y,
+ }
+ })
+ )
+ }
+
+ // Zoom out to fit the shapes, if necessary
+ selectionPageBounds = editor.getSelectionPageBounds()
+ if (selectionPageBounds && !viewportPageBounds.contains(selectionPageBounds)) {
+ editor.zoomToSelection()
+ }
+}
+
+export function createEmptyBookmarkShape(
+ editor: Editor,
+ url: string,
+ position: VecLike
+): TLBookmarkShape {
+ const partial: TLShapePartial = {
+ id: createShapeId(),
+ type: 'bookmark',
+ x: position.x - 150,
+ y: position.y - 160,
+ opacity: 1,
+ props: {
+ assetId: null,
+ url,
+ },
+ }
+
+ editor.batch(() => {
+ editor.createShapes([partial]).select(partial.id)
+ centerSelectionAroundPoint(editor, position)
+ })
+
+ return editor.getShape(partial.id) as TLBookmarkShape
+} \ No newline at end of file
diff --git a/apps/web/app/(canvas)/lib/loadSnap.ts b/apps/web/app/(canvas)/lib/loadSnap.ts
new file mode 100644
index 00000000..15aad998
--- /dev/null
+++ b/apps/web/app/(canvas)/lib/loadSnap.ts
@@ -0,0 +1,13 @@
+import { createTLStore, defaultShapeUtils } from "tldraw";
+import { twitterCardUtil } from "../twitterCard";
+export async function loadRemoteSnapshot() {
+ const res = await fetch(
+ "https://learning-cf.pruthvirajthinks.workers.dev/get/page3",
+ );
+ const snapshot = JSON.parse(await res.json());
+ const newStore = createTLStore({
+ shapeUtils: [...defaultShapeUtils, twitterCardUtil],
+ });
+ newStore.loadSnapshot(snapshot);
+ return newStore;
+} \ No newline at end of file
diff --git a/apps/web/app/(canvas)/savesnap.tsx b/apps/web/app/(canvas)/savesnap.tsx
new file mode 100644
index 00000000..f82e97e3
--- /dev/null
+++ b/apps/web/app/(canvas)/savesnap.tsx
@@ -0,0 +1,43 @@
+import { useCallback, useEffect, useState } from "react";
+import { debounce, useEditor } from "tldraw";
+
+export function SaveStatus() {
+ const [save, setSave] = useState("saved!");
+ const editor = useEditor();
+
+ const debouncedSave = useCallback(
+ debounce(async () => {
+ const snapshot = editor.store.getSnapshot();
+ localStorage.setItem("saved", JSON.stringify(snapshot));
+
+ const res = await fetch(
+ "https://learning-cf.pruthvirajthinks.workers.dev/post/page3",
+ {
+ method: "POST",
+ headers: { "Content-Type": "application/json" },
+ body: JSON.stringify({
+ data: snapshot,
+ }),
+ },
+ );
+
+ console.log(await res.json());
+ setSave("saved!");
+ }, 3000),
+ [editor], // Dependency array ensures the function is not recreated on every render
+ );
+
+ useEffect(() => {
+ const unsubscribe = editor.store.listen(
+ () => {
+ setSave("saving...");
+ debouncedSave();
+ },
+ { scope: "document", source: "user" },
+ );
+
+ return () => unsubscribe(); // Cleanup on unmount
+ }, [editor, debouncedSave]);
+
+ return <button>{save}</button>;
+} \ No newline at end of file
diff --git a/apps/web/app/(canvas)/svg.tsx b/apps/web/app/(canvas)/svg.tsx
new file mode 100644
index 00000000..bae4e614
--- /dev/null
+++ b/apps/web/app/(canvas)/svg.tsx
@@ -0,0 +1,97 @@
+export function SettingsSvg() {
+ return (
+ <svg
+ width="16"
+ height="18"
+ viewBox="0 0 16 18"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M7.2321 0.875C6.46793 0.875 5.81627 1.4275 5.69043 2.18083L5.5421 3.07417C5.52543 3.17417 5.44627 3.29083 5.2946 3.36417C5.00906 3.50143 4.73438 3.66022 4.47293 3.83917C4.3346 3.935 4.1946 3.94417 4.09793 3.90833L3.25043 3.59C2.90397 3.4602 2.52268 3.45755 2.17445 3.58253C1.82621 3.70751 1.53362 3.95201 1.34877 4.2725L0.580434 5.60333C0.395508 5.92363 0.330196 6.29915 0.396115 6.66307C0.462034 7.027 0.65491 7.35575 0.940434 7.59083L1.64043 8.1675C1.7196 8.2325 1.7821 8.35833 1.76877 8.52583C1.74502 8.84178 1.74502 9.15906 1.76877 9.475C1.78127 9.64167 1.7196 9.76833 1.64127 9.83333L0.940434 10.41C0.65491 10.6451 0.462034 10.9738 0.396115 11.3378C0.330196 11.7017 0.395508 12.0772 0.580434 12.3975L1.34877 13.7283C1.53376 14.0487 1.8264 14.293 2.17462 14.4178C2.52285 14.5426 2.90406 14.5399 3.25043 14.41L4.0996 14.0917C4.19543 14.0558 4.33543 14.0658 4.4746 14.16C4.7346 14.3383 5.00877 14.4975 5.29543 14.635C5.4471 14.7083 5.52627 14.825 5.54293 14.9267L5.69127 15.8192C5.8171 16.5725 6.46877 17.125 7.23293 17.125H8.7696C9.53293 17.125 10.1854 16.5725 10.3113 15.8192L10.4596 14.9258C10.4763 14.8258 10.5546 14.7092 10.7071 14.635C10.9938 14.4975 11.2679 14.3383 11.5279 14.16C11.6671 14.065 11.8071 14.0558 11.9029 14.0917L12.7529 14.41C13.0992 14.5394 13.4801 14.5418 13.828 14.4168C14.1758 14.2919 14.4681 14.0476 14.6529 13.7275L15.4221 12.3967C15.607 12.0764 15.6723 11.7009 15.6064 11.3369C15.5405 10.973 15.3476 10.6443 15.0621 10.4092L14.3621 9.8325C14.2829 9.7675 14.2204 9.64167 14.2338 9.47417C14.2575 9.15822 14.2575 8.84095 14.2338 8.525C14.2204 8.35833 14.2829 8.23167 14.3613 8.16667L15.0613 7.59C15.6513 7.105 15.8038 6.265 15.4221 5.6025L14.6538 4.27167C14.4688 3.95132 14.1761 3.707 13.8279 3.58218C13.4797 3.45735 13.0985 3.46013 12.7521 3.59L11.9021 3.90833C11.8071 3.94417 11.6671 3.93417 11.5279 3.83917C11.2668 3.66025 10.9924 3.50145 10.7071 3.36417C10.5546 3.29167 10.4763 3.175 10.4596 3.07417L10.3104 2.18083C10.2497 1.81589 10.0614 1.48435 9.77905 1.24522C9.49674 1.0061 9.13874 0.874907 8.76877 0.875H7.23293H7.2321ZM8.00043 12.125C8.82923 12.125 9.62409 11.7958 10.2101 11.2097C10.7962 10.6237 11.1254 9.8288 11.1254 9C11.1254 8.1712 10.7962 7.37634 10.2101 6.79029C9.62409 6.20424 8.82923 5.875 8.00043 5.875C7.17163 5.875 6.37678 6.20424 5.79072 6.79029C5.20467 7.37634 4.87543 8.1712 4.87543 9C4.87543 9.8288 5.20467 10.6237 5.79072 11.2097C6.37678 11.7958 7.17163 12.125 8.00043 12.125Z"
+ fill="#989EA4"
+ />
+ </svg>
+ );
+}
+
+export function DragSvg() {
+ return (
+ <svg
+ width="6"
+ height="9"
+ viewBox="0 0 6 9"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ fill-rule="evenodd"
+ clip-rule="evenodd"
+ d="M4.78829 0.916134C4.78348 0.920945 4.77696 0.923648 4.77015 0.923648C4.76335 0.923648 4.75682 0.920945 4.75201 0.916134C4.7472 0.911322 4.74449 0.904797 4.74449 0.897991C4.74449 0.891186 4.7472 0.884661 4.75201 0.879849C4.75682 0.875038 4.76335 0.872335 4.77015 0.872335C4.77696 0.872335 4.78348 0.875038 4.78829 0.879849C4.79311 0.884661 4.79581 0.891186 4.79581 0.897991C4.79581 0.904797 4.79311 0.911322 4.78829 0.916134ZM4.77015 0C4.53199 0 4.30358 0.0946096 4.13518 0.263016C3.96677 0.431421 3.87216 0.659829 3.87216 0.897991C3.87216 1.13615 3.96677 1.36456 4.13518 1.53297C4.30358 1.70137 4.53199 1.79598 4.77015 1.79598C5.00831 1.79598 5.23672 1.70137 5.40513 1.53297C5.57353 1.36456 5.66814 1.13615 5.66814 0.897991C5.66814 0.659829 5.57353 0.431421 5.40513 0.263016C5.23672 0.0946096 5.00831 0 4.77015 0ZM4.78829 4.40547C4.78348 4.41028 4.77696 4.41299 4.77015 4.41299C4.76335 4.41299 4.75682 4.41028 4.75201 4.40547C4.7472 4.40066 4.74449 4.39414 4.74449 4.38733C4.74449 4.38052 4.7472 4.374 4.75201 4.36919C4.75682 4.36438 4.76335 4.36167 4.77015 4.36167C4.77696 4.36167 4.78348 4.36438 4.78829 4.36919C4.79311 4.374 4.79581 4.38052 4.79581 4.38733C4.79581 4.39414 4.79311 4.40066 4.78829 4.40547ZM4.77015 3.48934C4.53199 3.48934 4.30358 3.58395 4.13518 3.75235C3.96677 3.92076 3.87216 4.14917 3.87216 4.38733C3.87216 4.62549 3.96677 4.8539 4.13518 5.02231C4.30358 5.19071 4.53199 5.28532 4.77015 5.28532C5.00831 5.28532 5.23672 5.19071 5.40513 5.02231C5.57353 4.8539 5.66814 4.62549 5.66814 4.38733C5.66814 4.14917 5.57353 3.92076 5.40513 3.75235C5.23672 3.58395 5.00831 3.48934 4.77015 3.48934ZM4.78829 7.89481C4.78348 7.89962 4.77696 7.90232 4.77015 7.90232C4.76335 7.90232 4.75682 7.89962 4.75201 7.89481C4.7472 7.89 4.74449 7.88347 4.74449 7.87667C4.74449 7.86986 4.7472 7.86334 4.75201 7.85853C4.75682 7.85371 4.76335 7.85101 4.77015 7.85101C4.77696 7.85101 4.78348 7.85371 4.78829 7.85853C4.79311 7.86334 4.79581 7.86986 4.79581 7.87667C4.79581 7.88347 4.79311 7.89 4.78829 7.89481ZM4.77015 6.97868C4.53199 6.97868 4.30358 7.07329 4.13518 7.24169C3.96677 7.4101 3.87216 7.63851 3.87216 7.87667C3.87216 8.11483 3.96677 8.34324 4.13518 8.51164C4.30358 8.68005 4.53199 8.77466 4.77015 8.77466C5.00831 8.77466 5.23672 8.68005 5.40513 8.51164C5.57353 8.34324 5.66814 8.11483 5.66814 7.87667C5.66814 7.63851 5.57353 7.4101 5.40513 7.24169C5.23672 7.07329 5.00831 6.97868 4.77015 6.97868ZM0.916134 0.91702C0.911322 0.921832 0.904796 0.924535 0.897991 0.924535C0.891187 0.924535 0.884661 0.921832 0.879849 0.91702C0.875038 0.912209 0.872335 0.905683 0.872335 0.898878C0.872335 0.892073 0.875038 0.885547 0.879849 0.880736C0.884661 0.875924 0.891187 0.873221 0.897991 0.873221C0.904796 0.873221 0.911322 0.875924 0.916134 0.880736C0.920945 0.885547 0.923648 0.892073 0.923648 0.898878C0.923648 0.905683 0.920945 0.912209 0.916134 0.91702ZM0.897991 0.000886679C0.659829 0.000886679 0.431422 0.0954962 0.263016 0.263902C0.0946102 0.432308 0 0.660715 0 0.898878C0 1.13704 0.0946102 1.36545 0.263016 1.53385C0.431422 1.70226 0.659829 1.79687 0.897991 1.79687C1.13615 1.79687 1.36456 1.70226 1.53297 1.53385C1.70137 1.36545 1.79598 1.13704 1.79598 0.898878C1.79598 0.660715 1.70137 0.432308 1.53297 0.263902C1.36456 0.0954962 1.13615 0.000886679 0.897991 0.000886679ZM0.916134 4.40636C0.911323 4.41117 0.904797 4.41387 0.897991 4.41387C0.891186 4.41387 0.88466 4.41117 0.879849 4.40636C0.875038 4.40155 0.872335 4.39502 0.872335 4.38822C0.872335 4.38141 0.875038 4.37489 0.879849 4.37007C0.88466 4.36526 0.891186 4.36256 0.897991 4.36256C0.904797 4.36256 0.911323 4.36526 0.916134 4.37007C0.920945 4.37489 0.923648 4.38141 0.923648 4.38822C0.923648 4.39502 0.920945 4.40155 0.916134 4.40636ZM0.897991 3.49022C0.659828 3.49022 0.431421 3.58484 0.263016 3.75324C0.0946104 3.92165 0 4.15005 0 4.38822C0 4.62638 0.0946104 4.85479 0.263016 5.02319C0.431421 5.1916 0.659828 5.28621 0.897991 5.28621C1.13615 5.28621 1.36456 5.1916 1.53297 5.02319C1.70137 4.85479 1.79598 4.62638 1.79598 4.38822C1.79598 4.15005 1.70137 3.92165 1.53297 3.75324C1.36456 3.58484 1.13615 3.49022 0.897991 3.49022ZM0.916134 7.8957C0.911322 7.90051 0.904795 7.90321 0.897991 7.90321C0.891187 7.90321 0.884661 7.90051 0.879849 7.8957C0.875038 7.89089 0.872335 7.88436 0.872335 7.87755C0.872335 7.87075 0.875038 7.86422 0.879849 7.85941C0.884661 7.8546 0.891187 7.8519 0.897991 7.8519C0.904795 7.8519 0.911322 7.8546 0.916134 7.85941C0.920945 7.86422 0.923648 7.87075 0.923648 7.87755C0.923648 7.88436 0.920945 7.89089 0.916134 7.8957ZM0.897991 6.97956C0.65983 6.97956 0.431422 7.07417 0.263016 7.24258C0.0946099 7.41098 0 7.63939 0 7.87755C0 8.11572 0.0946099 8.34412 0.263016 8.51253C0.431422 8.68094 0.65983 8.77555 0.897991 8.77555C1.13615 8.77555 1.36456 8.68094 1.53297 8.51253C1.70137 8.34412 1.79598 8.11572 1.79598 7.87755C1.79598 7.63939 1.70137 7.41098 1.53297 7.24258C1.36456 7.07417 1.13615 6.97956 0.897991 6.97956Z"
+ fill="#989EA4"
+ />
+ </svg>
+ );
+}
+
+export function TextLoadingSvg() {
+ return (
+ <svg
+ width="34"
+ height="24"
+ viewBox="0 0 14 10"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M0.8125 1.0625H13.1875M0.8125 5H13.1875M0.8125 8.9375H7"
+ stroke="#989EA4"
+ stroke-width="1.5"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ />
+ </svg>
+ );
+}
+
+export function ThreeDBlock() {
+ return (
+ <svg
+ width="32"
+ height="36"
+ viewBox="0 0 16 18"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M14.75 5.625L8 1.6875L1.25 5.625M14.75 5.625L8 9.5625M14.75 5.625V12.375L8 16.3125M1.25 5.625L8 9.5625M1.25 5.625V12.375L8 16.3125M8 9.5625V16.3125"
+ stroke="#989EA4"
+ stroke-width="1.5"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ />
+ </svg>
+ );
+}
+
+export function LinkSvg() {
+ return (
+ <svg
+ width="36"
+ height="36"
+ viewBox="0 0 18 18"
+ fill="none"
+ xmlns="http://www.w3.org/2000/svg"
+ >
+ <path
+ d="M9.89252 6.51602C10.3799 6.74871 10.8043 7.09497 11.1301 7.5257C11.4559 7.95643 11.6736 8.45906 11.7648 8.99136C11.8561 9.52366 11.8183 10.0701 11.6546 10.5848C11.4909 11.0994 11.206 11.5673 10.824 11.949L7.44902 15.324C6.81608 15.957 5.95763 16.3125 5.06252 16.3125C4.16741 16.3125 3.30896 15.957 2.67602 15.324C2.04308 14.6911 1.6875 13.8326 1.6875 12.9375C1.6875 12.0424 2.04308 11.184 2.67602 10.551L3.99377 9.23327M14.0063 8.76677L15.324 7.44902C15.957 6.81608 16.3125 5.95763 16.3125 5.06252C16.3125 4.16741 15.957 3.30896 15.324 2.67602C14.6911 2.04308 13.8326 1.6875 12.9375 1.6875C12.0424 1.6875 11.184 2.04308 10.551 2.67602L7.17602 6.05102C6.794 6.43277 6.50917 6.90063 6.34546 7.41529C6.18175 7.92995 6.14393 8.47638 6.2352 9.00868C6.32646 9.54098 6.54414 10.0436 6.86994 10.4743C7.19574 10.9051 7.62015 11.2513 8.10752 11.484"
+ stroke="#989EA4"
+ stroke-width="1.5"
+ stroke-linecap="round"
+ stroke-linejoin="round"
+ />
+ </svg>
+ );
+}
diff --git a/apps/web/app/(canvas)/twitterCard.tsx b/apps/web/app/(canvas)/twitterCard.tsx
new file mode 100644
index 00000000..c5582a98
--- /dev/null
+++ b/apps/web/app/(canvas)/twitterCard.tsx
@@ -0,0 +1,84 @@
+import { BaseBoxShapeUtil, HTMLContainer, TLBaseShape, toDomPrecision } from "tldraw";
+
+type ITwitterCardShape = TLBaseShape<
+ "Twittercard",
+ { w: number; h: number; url: string }
+>;
+
+export class twitterCardUtil extends BaseBoxShapeUtil<ITwitterCardShape> {
+ static override type = "Twittercard" as const;
+
+ getDefaultProps(): ITwitterCardShape["props"] {
+ return {
+ w: 500,
+ h: 550,
+ url: "",
+ };
+ }
+
+ component(s: ITwitterCardShape) {
+ return (
+ <HTMLContainer className="flex h-full w-full items-center justify-center">
+ <TwitterPost
+ url={s.props.url}
+ width={s.props.w}
+ isInteractive={false}
+ height={s.props.h}
+ />
+ </HTMLContainer>
+ );
+ }
+
+ indicator(shape: ITwitterCardShape) {
+ return <rect width={shape.props.w} height={shape.props.h} />;
+ }
+}
+
+function TwitterPost({
+ isInteractive,
+ width,
+ height,
+ url,
+}: {
+ isInteractive: boolean;
+ width: number;
+ height: number;
+ url: string;
+}) {
+ const link = (() => {
+ try {
+ const urlObj = new URL(url);
+ const path = urlObj.pathname;
+ return path;
+ } catch (error) {
+ console.error("Invalid URL", error);
+ return null;
+ }
+ })();
+
+ return (
+ <iframe
+ className="tl-embed"
+ draggable={false}
+ width={toDomPrecision(width)}
+ height={toDomPrecision(height)}
+ seamless
+ referrerPolicy="no-referrer-when-downgrade"
+ style={{
+ pointerEvents: isInteractive ? "all" : "none",
+ zIndex: isInteractive ? "" : "-1",
+ }}
+ srcDoc={`
+ <html lang="en">
+ <head>
+ <meta charset="UTF-8">
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
+ <title>Document</title>
+ </head>
+ <body>
+ <blockquote data-theme="dark" class="twitter-tweet"><p lang="en" dir="ltr"><a href="https://twitter.com${link}"></a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>
+ </body>
+ </html>`}
+ />
+ );
+}
diff --git a/apps/web/app/(dash)/actions.ts b/apps/web/app/(dash)/actions.ts
deleted file mode 100644
index 70c2a567..00000000
--- a/apps/web/app/(dash)/actions.ts
+++ /dev/null
@@ -1,48 +0,0 @@
-"use server";
-
-import { cookies, headers } from "next/headers";
-import { db } from "../helpers/server/db";
-import { sessions, users, space } from "../helpers/server/db/schema";
-import { eq } from "drizzle-orm";
-import { redirect } from "next/navigation";
-
-export async function ensureAuth() {
- const token =
- cookies().get("next-auth.session-token")?.value ??
- cookies().get("__Secure-authjs.session-token")?.value ??
- cookies().get("authjs.session-token")?.value ??
- headers().get("Authorization")?.replace("Bearer ", "");
-
- if (!token) {
- return undefined;
- }
-
- const sessionData = await db
- .select()
- .from(sessions)
- .innerJoin(users, eq(users.id, sessions.userId))
- .where(eq(sessions.sessionToken, token));
-
- if (!sessionData || sessionData.length < 0) {
- return undefined;
- }
-
- return {
- user: sessionData[0]!.user,
- session: sessionData[0]!,
- };
-}
-
-export async function getSpaces() {
- const data = await ensureAuth();
- if (!data) {
- redirect("/signin");
- }
-
- const sp = await db
- .select()
- .from(space)
- .where(eq(space.user, data.user.email));
-
- return sp;
-}
diff --git a/apps/web/app/(dash)/chat/CodeBlock.tsx b/apps/web/app/(dash)/chat/CodeBlock.tsx
new file mode 100644
index 00000000..0bb6a19d
--- /dev/null
+++ b/apps/web/app/(dash)/chat/CodeBlock.tsx
@@ -0,0 +1,90 @@
+import React, { useRef, useState } from "react";
+
+const CodeBlock = ({
+ lang,
+ codeChildren,
+}: {
+ lang: string;
+ codeChildren: React.ReactNode & React.ReactNode[];
+}) => {
+ const codeRef = useRef<HTMLElement>(null);
+
+ return (
+ <div className="bg-black rounded-md">
+ <CodeBar lang={lang} codeRef={codeRef} />
+ <div className="p-4 overflow-y-auto">
+ <code ref={codeRef} className={`!whitespace-pre hljs language-${lang}`}>
+ {codeChildren}
+ </code>
+ </div>
+ </div>
+ );
+};
+
+const CodeBar = React.memo(
+ ({
+ lang,
+ codeRef,
+ }: {
+ lang: string;
+ codeRef: React.RefObject<HTMLElement>;
+ }) => {
+ const [isCopied, setIsCopied] = useState<boolean>(false);
+ return (
+ <div className="flex items-center relative text-gray-200 bg-gray-800 px-4 py-2 text-xs font-sans">
+ <span className="">{lang}</span>
+ <button
+ className="flex ml-auto gap-2"
+ aria-label="copy codeblock"
+ onClick={async () => {
+ const codeString = codeRef.current?.textContent;
+ if (codeString)
+ navigator.clipboard.writeText(codeString).then(() => {
+ setIsCopied(true);
+ setTimeout(() => setIsCopied(false), 3000);
+ });
+ }}
+ >
+ {isCopied ? (
+ <>
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ fill="none"
+ viewBox="0 0 24 24"
+ strokeWidth={1.5}
+ stroke="currentColor"
+ className="size-4"
+ >
+ <path
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ d="M11.35 3.836c-.065.21-.1.433-.1.664 0 .414.336.75.75.75h4.5a.75.75 0 0 0 .75-.75 2.25 2.25 0 0 0-.1-.664m-5.8 0A2.251 2.251 0 0 1 13.5 2.25H15c1.012 0 1.867.668 2.15 1.586m-5.8 0c-.376.023-.75.05-1.124.08C9.095 4.01 8.25 4.973 8.25 6.108V8.25m8.9-4.414c.376.023.75.05 1.124.08 1.131.094 1.976 1.057 1.976 2.192V16.5A2.25 2.25 0 0 1 18 18.75h-2.25m-7.5-10.5H4.875c-.621 0-1.125.504-1.125 1.125v11.25c0 .621.504 1.125 1.125 1.125h9.75c.621 0 1.125-.504 1.125-1.125V18.75m-7.5-10.5h6.375c.621 0 1.125.504 1.125 1.125v9.375m-8.25-3 1.5 1.5 3-3.75"
+ />
+ </svg>
+ Copied!
+ </>
+ ) : (
+ <>
+ <svg
+ xmlns="http://www.w3.org/2000/svg"
+ fill="none"
+ viewBox="0 0 24 24"
+ strokeWidth={1.5}
+ stroke="currentColor"
+ className="size-4"
+ >
+ <path
+ strokeLinecap="round"
+ strokeLinejoin="round"
+ d="M15.75 17.25v3.375c0 .621-.504 1.125-1.125 1.125h-9.75a1.125 1.125 0 0 1-1.125-1.125V7.875c0-.621.504-1.125 1.125-1.125H6.75a9.06 9.06 0 0 1 1.5.124m7.5 10.376h3.375c.621 0 1.125-.504 1.125-1.125V11.25c0-4.46-3.243-8.161-7.5-8.876a9.06 9.06 0 0 0-1.5-.124H9.375c-.621 0-1.125.504-1.125 1.125v3.5m7.5 10.375H9.375a1.125 1.125 0 0 1-1.125-1.125v-9.25m12 6.625v-1.875a3.375 3.375 0 0 0-3.375-3.375h-1.5a1.125 1.125 0 0 1-1.125-1.125v-1.5a3.375 3.375 0 0 0-3.375-3.375H9.75"
+ />
+ </svg>
+ Copy code
+ </>
+ )}
+ </button>
+ </div>
+ );
+ },
+);
+export default CodeBlock;
diff --git a/apps/web/app/(dash)/chat/actions.ts b/apps/web/app/(dash)/chat/actions.ts
index 908fe79e..e69de29b 100644
--- a/apps/web/app/(dash)/chat/actions.ts
+++ b/apps/web/app/(dash)/chat/actions.ts
@@ -1 +0,0 @@
-"use server";
diff --git a/apps/web/app/(dash)/chat/chatWindow.tsx b/apps/web/app/(dash)/chat/chatWindow.tsx
index 43c337ee..77c1f32b 100644
--- a/apps/web/app/(dash)/chat/chatWindow.tsx
+++ b/apps/web/app/(dash)/chat/chatWindow.tsx
@@ -6,29 +6,147 @@ import QueryInput from "../home/queryinput";
import { cn } from "@repo/ui/lib/utils";
import { motion } from "framer-motion";
import { useRouter } from "next/navigation";
+import { ChatHistory } from "@repo/shared-types";
+import {
+ Accordion,
+ AccordionContent,
+ AccordionItem,
+ AccordionTrigger,
+} from "@repo/ui/shadcn/accordion";
+import Markdown from "react-markdown";
+import remarkGfm from "remark-gfm";
+import remarkMath from "remark-math";
+import rehypeKatex from "rehype-katex";
+import rehypeHighlight from "rehype-highlight";
+import { code, p } from "./markdownRenderHelpers";
+import { codeLanguageSubset } from "@/lib/constants";
+import { z } from "zod";
+import { toast } from "sonner";
+import Link from "next/link";
+import { sources } from "next/dist/compiled/webpack/webpack";
-function ChatWindow({ q }: { q: string }) {
+function ChatWindow({
+ q,
+ spaces,
+}: {
+ q: string;
+ spaces: { id: string; name: string }[];
+}) {
const [layout, setLayout] = useState<"chat" | "initial">("initial");
+ const [chatHistory, setChatHistory] = useState<ChatHistory[]>([
+ {
+ question: q,
+ answer: {
+ parts: [
+ // {
+ // text: `It seems like there might be a typo in your question. Could you please clarify or provide more context? If you meant "interesting," please let me know what specific information or topic you find interesting, and I can help you with that.`,
+ // },
+ ],
+ sources: [],
+ },
+ },
+ ]);
const router = useRouter();
+ const getAnswer = async (query: string, spaces: string[]) => {
+ const sourcesFetch = await fetch(
+ `/api/chat?q=${query}&spaces=${spaces}&sourcesOnly=true`,
+ {
+ method: "POST",
+ body: JSON.stringify({ chatHistory }),
+ },
+ );
+
+ // TODO: handle this properly
+ const sources = await sourcesFetch.json();
+
+ const sourcesZod = z.object({
+ ids: z.array(z.string()),
+ metadata: z.array(z.any()),
+ });
+
+ const sourcesParsed = sourcesZod.safeParse(sources);
+
+ if (!sourcesParsed.success) {
+ console.log(sources);
+ console.error(sourcesParsed.error);
+ toast.error("Something went wrong while getting the sources");
+ return;
+ }
+
+ setChatHistory((prevChatHistory) => {
+ const newChatHistory = [...prevChatHistory];
+ const lastAnswer = newChatHistory[newChatHistory.length - 1];
+ if (!lastAnswer) return prevChatHistory;
+ const filteredSourceUrls = new Set(
+ sourcesParsed.data.metadata.map((source) => source.url),
+ );
+ const uniqueSources = sourcesParsed.data.metadata.filter((source) => {
+ if (filteredSourceUrls.has(source.url)) {
+ filteredSourceUrls.delete(source.url);
+ return true;
+ }
+ return false;
+ });
+ lastAnswer.answer.sources = uniqueSources.map((source) => ({
+ title: source.title ?? "Untitled",
+ type: source.type ?? "page",
+ source: source.url ?? "https://supermemory.ai",
+ content: source.content ?? "No content available",
+ numChunks: sourcesParsed.data.metadata.filter(
+ (f) => f.url === source.url,
+ ).length,
+ }));
+ return newChatHistory;
+ });
+
+ const resp = await fetch(`/api/chat?q=${query}&spaces=${spaces}`, {
+ method: "POST",
+ body: JSON.stringify({ chatHistory }),
+ });
+
+ const reader = resp.body?.getReader();
+ let done = false;
+ let result = "";
+ while (!done && reader) {
+ const { value, done: d } = await reader.read();
+ done = d;
+
+ setChatHistory((prevChatHistory) => {
+ const newChatHistory = [...prevChatHistory];
+ const lastAnswer = newChatHistory[newChatHistory.length - 1];
+ if (!lastAnswer) return prevChatHistory;
+ lastAnswer.answer.parts.push({ text: new TextDecoder().decode(value) });
+ return newChatHistory;
+ });
+ }
+
+ console.log(result);
+ };
+
useEffect(() => {
- if (q !== "") {
+ if (q.trim().length > 0) {
+ getAnswer(
+ q,
+ spaces.map((s) => s.id),
+ );
setTimeout(() => {
setLayout("chat");
}, 300);
} else {
router.push("/home");
}
- }, [q]);
+ }, []);
+
return (
- <div>
+ <div className="h-full">
<AnimatePresence mode="popLayout">
{layout === "initial" ? (
<motion.div
exit={{ opacity: 0 }}
key="initial"
- className="max-w-3xl flex mx-auto w-full flex-col"
+ className="max-w-3xl h-full justify-center items-center flex mx-auto w-full flex-col"
>
<div className="w-full h-96">
<QueryInput initialQuery={q} initialSpaces={[]} disabled />
@@ -36,16 +154,121 @@ function ChatWindow({ q }: { q: string }) {
</motion.div>
) : (
<div
- className="max-w-3xl flex mx-auto w-full flex-col mt-8"
+ className="max-w-3xl flex mx-auto w-full flex-col mt-24"
key="chat"
>
- <h2
- className={cn(
- "transition-all transform translate-y-0 opacity-100 duration-500 ease-in-out font-semibold text-2xl",
- )}
- >
- {q}
- </h2>
+ {chatHistory.map((chat, idx) => (
+ <div
+ key={idx}
+ className={`mt-8 ${idx != chatHistory.length - 1 ? "pb-2 border-b" : ""}`}
+ >
+ <h2
+ className={cn(
+ "text-white transition-all transform translate-y-0 opacity-100 duration-500 ease-in-out font-semibold text-2xl",
+ )}
+ >
+ {chat.question}
+ </h2>
+
+ <div className="flex flex-col gap-2 mt-2">
+ <div
+ className={`${chat.answer.sources.length > 0 || chat.answer.parts.length === 0 ? "flex" : "hidden"}`}
+ >
+ <Accordion
+ defaultValue={
+ idx === chatHistory.length - 1 ? "memories" : ""
+ }
+ type="single"
+ collapsible
+ >
+ <AccordionItem value="memories">
+ <AccordionTrigger className="text-foreground-menu">
+ Related Memories
+ </AccordionTrigger>
+ {/* TODO: fade out content on the right side, the fade goes away when the user scrolls */}
+ <AccordionContent
+ className="relative flex gap-2 max-w-3xl overflow-auto no-scrollbar"
+ defaultChecked
+ >
+ {/* Loading state */}
+ {chat.answer.sources.length > 0 ||
+ (chat.answer.parts.length === 0 && (
+ <>
+ {[1, 2, 3, 4].map((_, idx) => (
+ <div
+ key={`loadingState-${idx}`}
+ className="rounded-xl bg-secondary p-4 flex flex-col gap-2 min-w-72 animate-pulse"
+ >
+ <div className="bg-slate-700 h-2 rounded-full w-1/2"></div>
+ <div className="bg-slate-700 h-2 rounded-full w-full"></div>
+ </div>
+ ))}
+ </>
+ ))}
+ {chat.answer.sources.map((source, idx) => (
+ <Link
+ href={source.source}
+ key={idx}
+ className="rounded-xl bg-secondary p-4 flex flex-col gap-2 min-w-72"
+ >
+ <div className="flex justify-between text-foreground-menu text-sm">
+ <span>{source.type}</span>
+
+ {source.numChunks > 1 && (
+ <span>{source.numChunks} chunks</span>
+ )}
+ </div>
+ <div className="text-base">{source.title}</div>
+ <div className="text-xs">
+ {source.content.length > 100
+ ? source.content.slice(0, 100) + "..."
+ : source.content}
+ </div>
+ </Link>
+ ))}
+ </AccordionContent>
+ </AccordionItem>
+ </Accordion>
+ </div>
+
+ {/* Summary */}
+ <div>
+ <div className="text-foreground-menu py-2">Summary</div>
+ <div className="text-base">
+ {chat.answer.parts.length === 0 && (
+ <div className="animate-pulse flex space-x-4">
+ <div className="flex-1 space-y-3 py-1">
+ <div className="h-2 bg-slate-700 rounded"></div>
+ <div className="h-2 bg-slate-700 rounded"></div>
+ </div>
+ </div>
+ )}
+ <Markdown
+ remarkPlugins={[remarkGfm, [remarkMath]]}
+ rehypePlugins={[
+ rehypeKatex,
+ [
+ rehypeHighlight,
+ {
+ detect: true,
+ ignoreMissing: true,
+ subset: codeLanguageSubset,
+ },
+ ],
+ ]}
+ components={{
+ code: code as any,
+ p: p as any,
+ }}
+ className="flex flex-col gap-2"
+ >
+ {chat.answer.parts.map((part) => part.text).join("")}
+ </Markdown>
+ </div>
+ </div>
+ </div>
+ </div>
+ ))}
</div>
)}
</AnimatePresence>
diff --git a/apps/web/app/(dash)/chat/markdownRenderHelpers.tsx b/apps/web/app/(dash)/chat/markdownRenderHelpers.tsx
new file mode 100644
index 00000000..747d4fca
--- /dev/null
+++ b/apps/web/app/(dash)/chat/markdownRenderHelpers.tsx
@@ -0,0 +1,25 @@
+import { DetailedHTMLProps, HTMLAttributes, memo } from "react";
+import { ExtraProps } from "react-markdown";
+import CodeBlock from "./CodeBlock";
+
+export const code = memo((props: JSX.IntrinsicElements["code"]) => {
+ const { className, children } = props;
+ const match = /language-(\w+)/.exec(className || "");
+ const lang = match && match[1];
+
+ return <CodeBlock lang={lang || "text"} codeChildren={children as any} />;
+});
+
+export const p = memo(
+ (
+ props?: Omit<
+ DetailedHTMLProps<
+ HTMLAttributes<HTMLParagraphElement>,
+ HTMLParagraphElement
+ >,
+ "ref"
+ >,
+ ) => {
+ return <p className="whitespace-pre-wrap">{props?.children}</p>;
+ },
+);
diff --git a/apps/web/app/(dash)/chat/page.tsx b/apps/web/app/(dash)/chat/page.tsx
index 9e28fda7..73519851 100644
--- a/apps/web/app/(dash)/chat/page.tsx
+++ b/apps/web/app/(dash)/chat/page.tsx
@@ -1,5 +1,7 @@
import ChatWindow from "./chatWindow";
-import { chatSearchParamsCache } from "../../helpers/lib/searchParams";
+import { chatSearchParamsCache } from "../../../lib/searchParams";
+// @ts-expect-error
+await import("katex/dist/katex.min.css");
function Page({
searchParams,
@@ -10,7 +12,7 @@ function Page({
console.log(spaces);
- return <ChatWindow q={q} />;
+ return <ChatWindow q={q} spaces={[]} />;
}
export default Page;
diff --git a/apps/web/app/(dash)/dynamicisland.tsx b/apps/web/app/(dash)/dynamicisland.tsx
new file mode 100644
index 00000000..6fa56fae
--- /dev/null
+++ b/apps/web/app/(dash)/dynamicisland.tsx
@@ -0,0 +1,373 @@
+"use client";
+
+import { AddIcon } from "@repo/ui/icons";
+import Image from "next/image";
+
+import { AnimatePresence, useMotionValueEvent, useScroll } from "framer-motion";
+import { useActionState, useEffect, useRef, useState } from "react";
+import { motion } from "framer-motion";
+import { Label } from "@repo/ui/shadcn/label";
+import { Input } from "@repo/ui/shadcn/input";
+import { Textarea } from "@repo/ui/shadcn/textarea";
+import { createMemory, createSpace } from "../actions/doers";
+import {
+ Select,
+ SelectContent,
+ SelectItem,
+ SelectTrigger,
+ SelectValue,
+} from "@repo/ui/shadcn/select";
+import { Space } from "../actions/types";
+import { getSpaces } from "../actions/fetchers";
+import { toast } from "sonner";
+import { useFormStatus } from "react-dom";
+
+export function DynamicIsland() {
+ const { scrollYProgress } = useScroll();
+ const [visible, setVisible] = useState(true);
+
+ useMotionValueEvent(scrollYProgress, "change", (current) => {
+ if (typeof current === "number") {
+ let direction = current! - scrollYProgress.getPrevious()!;
+
+ if (direction < 0 || direction === 1) {
+ setVisible(true);
+ } else {
+ setVisible(false);
+ }
+ }
+ });
+
+ return (
+ <div className="fixed z-40 left-1/2 -translate-x-1/2 top-12">
+ <AnimatePresence mode="wait">
+ <motion.div
+ initial={{
+ opacity: 1,
+ y: -150,
+ }}
+ animate={{
+ y: visible ? 0 : -150,
+ opacity: visible ? 1 : 0,
+ }}
+ transition={{
+ duration: 0.2,
+ }}
+ className="flex flex-col items-center"
+ >
+ <DynamicIslandContent />
+ </motion.div>
+ </AnimatePresence>
+ </div>
+ );
+}
+
+export default DynamicIsland;
+
+function DynamicIslandContent() {
+ const [show, setshow] = useState(true);
+ function cancelfn() {
+ setshow(true);
+ }
+
+ const lastBtn = useRef<string>();
+ useEffect(() => {
+ console.log(show);
+ }, [show]);
+
+ useEffect(() => {
+ document.addEventListener("keydown", (e) => {
+ if (e.key === "Escape") {
+ setshow(true);
+ }
+ console.log(e.key, lastBtn.current);
+ if (e.key === "a" && lastBtn.current === "Alt") {
+ setshow(false);
+ }
+ lastBtn.current = e.key;
+ });
+ }, []);
+ return (
+ <>
+ {show ? (
+ <div
+ onClick={() => setshow(!show)}
+ className="bg-secondary px-3 w-[2.23rem] overflow-hidden hover:w-[9.2rem] whitespace-nowrap py-2 rounded-3xl transition-[width] cursor-pointer"
+ >
+ <div className="flex gap-4 items-center">
+ <Image src={AddIcon} alt="Add icon" />
+ Add Content
+ </div>
+ </div>
+ ) : (
+ <div>
+ <ToolBar cancelfn={cancelfn} />
+ </div>
+ )}
+ </>
+ );
+}
+
+const fakeitems = ["spaces", "page", "note"];
+
+function ToolBar({ cancelfn }: { cancelfn: () => void }) {
+ const [spaces, setSpaces] = useState<Space[]>([]);
+
+ const [index, setIndex] = useState(0);
+
+ useEffect(() => {
+ (async () => {
+ let spaces = await getSpaces();
+
+ if (!spaces.success || !spaces.data) {
+ toast.warning("Unable to get spaces", {
+ richColors: true,
+ });
+ setSpaces([]);
+ return;
+ }
+ setSpaces(spaces.data);
+ })();
+ }, []);
+
+ return (
+ <AnimatePresence mode="wait">
+ <motion.div
+ initial={{
+ opacity: 0,
+ y: 20,
+ }}
+ animate={{
+ y: 0,
+ opacity: 1,
+ }}
+ exit={{
+ opacity: 0,
+ y: 20,
+ }}
+ transition={{
+ duration: 0.2,
+ }}
+ className="flex flex-col items-center"
+ >
+ <div className="bg-secondary py-[.35rem] px-[.6rem] rounded-2xl">
+ <HoverEffect
+ items={fakeitems}
+ index={index}
+ indexFn={(i) => setIndex(i)}
+ />
+ </div>
+ {index === 0 ? (
+ <SpaceForm cancelfn={cancelfn} />
+ ) : index === 1 ? (
+ <PageForm cancelfn={cancelfn} spaces={spaces} />
+ ) : (
+ <NoteForm cancelfn={cancelfn} spaces={spaces} />
+ )}
+ </motion.div>
+ </AnimatePresence>
+ );
+}
+
+export const HoverEffect = ({
+ items,
+ index,
+ indexFn,
+}: {
+ items: string[];
+ index: number;
+ indexFn: (i: number) => void;
+}) => {
+ return (
+ <div className={"flex"}>
+ {items.map((item, idx) => (
+ <button
+ key={idx}
+ className="relative block h-full w-full px-2 py-1"
+ onClick={() => indexFn(idx)}
+ >
+ <AnimatePresence>
+ {index === idx && (
+ <motion.span
+ className="absolute inset-0 block h-full w-full rounded-xl bg-[#2B3237]"
+ layoutId="hoverBackground"
+ initial={{ opacity: 0 }}
+ animate={{
+ opacity: 1,
+ transition: { duration: 0.15 },
+ }}
+ exit={{
+ opacity: 0,
+ transition: { duration: 0.15, delay: 0.2 },
+ }}
+ />
+ )}
+ </AnimatePresence>
+ <h3 className="text-[#858B92] z-50 relative">{item}</h3>
+ </button>
+ ))}
+ </div>
+ );
+};
+
+function SpaceForm({ cancelfn }: { cancelfn: () => void }) {
+ return (
+ <form
+ action={createSpace}
+ className="bg-secondary border border-muted-foreground px-4 py-3 rounded-2xl mt-2 flex flex-col gap-3"
+ >
+ <div>
+ <Label className="text-[#858B92]" htmlFor="name">
+ Name
+ </Label>
+ <Input
+ className="bg-[#2B3237] focus-visible:ring-0 border-none focus-visible:ring-offset-0"
+ id="name"
+ name="name"
+ />
+ </div>
+ <div className="flex justify-between">
+ <a className="text-blue-500" href="">
+ pull from store
+ </a>
+ {/* <div
+ onClick={cancelfn}
+ className="bg-[#2B3237] px-2 py-1 rounded-xl cursor-pointer"
+ >
+ cancel
+ </div> */}
+ <button
+ type="submit"
+ className="bg-[#2B3237] px-2 py-1 rounded-xl cursor-pointer"
+ >
+ Submit
+ </button>
+ </div>
+ </form>
+ );
+}
+
+function PageForm({
+ cancelfn,
+ spaces,
+}: {
+ cancelfn: () => void;
+ spaces: Space[];
+}) {
+ const [loading, setLoading] = useState(false);
+
+ const { pending } = useFormStatus();
+ return (
+ <form
+ action={async (e: FormData) => {
+ const content = e.get("content")?.toString();
+ const space = e.get("space")?.toString();
+ if (!content) {
+ toast.error("Content is required");
+ return;
+ }
+ setLoading(true);
+ const cont = await createMemory({
+ content: content,
+ spaces: space ? [space] : undefined,
+ });
+
+ console.log(cont);
+ setLoading(false);
+ if (cont.success) {
+ toast.success("Memory created");
+ } else {
+ toast.error("Memory creation failed");
+ }
+ }}
+ className="bg-secondary border border-muted-foreground px-4 py-3 rounded-2xl mt-2 flex flex-col gap-3"
+ >
+ <div>
+ <Label className="text-[#858B92]" htmlFor="space">
+ Space
+ </Label>
+ <Select name="space">
+ <SelectTrigger>
+ <SelectValue placeholder="Space" />
+ </SelectTrigger>
+ <SelectContent className="bg-secondary text-white">
+ {spaces.map((space) => (
+ <SelectItem key={space.id} value={space.id.toString()}>
+ {space.name}
+ </SelectItem>
+ ))}
+ </SelectContent>
+ </Select>
+ </div>
+ <div key={`${loading}-${pending}`}>
+ {loading ? <div>Loading...</div> : "not loading"}
+ </div>
+ <div>
+ <Label className="text-[#858B92]" htmlFor="name">
+ Page Url
+ </Label>
+ <Input
+ className="bg-[#2B3237] focus-visible:ring-0 border-none focus-visible:ring-offset-0"
+ id="input"
+ name="content"
+ />
+ </div>
+ <div className="flex justify-end">
+ <button
+ type="submit"
+ className="bg-[#2B3237] px-2 py-1 rounded-xl cursor-pointer"
+ >
+ Submit
+ </button>
+ </div>
+ </form>
+ );
+}
+
+function NoteForm({
+ cancelfn,
+ spaces,
+}: {
+ cancelfn: () => void;
+ spaces: Space[];
+}) {
+ return (
+ <div className="bg-secondary border border-muted-foreground px-4 py-3 rounded-2xl mt-2 flex flex-col gap-3">
+ <div>
+ <Label className="text-[#858B92]" htmlFor="name">
+ Space
+ </Label>
+ <Select>
+ <SelectTrigger>
+ <SelectValue placeholder="Space" />
+ </SelectTrigger>
+ <SelectContent className="bg-secondary text-white">
+ {spaces.map((space) => (
+ <SelectItem key={space.id} value={space.id.toString()}>
+ {space.name}
+ </SelectItem>
+ ))}
+ </SelectContent>
+ </Select>
+ </div>
+ <div>
+ <Label className="text-[#858B92]" htmlFor="name">
+ Note
+ </Label>
+ <Textarea
+ cols={4}
+ className="bg-[#2B3237] focus-visible:ring-0 border-none focus-visible:ring-offset-0 resize-none"
+ id="name"
+ />
+ </div>
+ <div className="flex justify-end">
+ <div
+ onClick={cancelfn}
+ className="bg-[#2B3237] px-2 py-1 rounded-xl cursor-pointer"
+ >
+ cancel
+ </div>
+ </div>
+ </div>
+ );
+}
diff --git a/apps/web/app/(dash)/header.tsx b/apps/web/app/(dash)/header.tsx
index 104c63bc..026cb080 100644
--- a/apps/web/app/(dash)/header.tsx
+++ b/apps/web/app/(dash)/header.tsx
@@ -2,49 +2,25 @@ import React from "react";
import Image from "next/image";
import Link from "next/link";
import Logo from "../../public/logo.svg";
-import { AddIcon, ChatIcon } from "@repo/ui/icons";
-import { Tabs, TabsContent, TabsList, TabsTrigger } from "@repo/ui/shadcn/tabs";
+import { ChatIcon } from "@repo/ui/icons";
+
+import DynamicIsland from "./dynamicisland";
function Header() {
return (
<div>
- <div className="flex items-center justify-between relative z-10">
- <Link href="/">
+ <div className="fixed left-0 w-full flex items-center justify-between z-10">
+ <Link className="px-5" href="/home">
<Image
src={Logo}
alt="SuperMemory logo"
- className="hover:brightness-125 duration-200"
+ className="hover:brightness-75 brightness-50 duration-200"
/>
</Link>
- <Tabs
- className="absolute flex flex-col justify-center items-center w-full -z-10 group top-0 transition-transform duration-1000 ease-out"
- defaultValue="account"
- >
- <div className="bg-secondary all-center h-11 rounded-full p-2 min-w-14">
- <button className="p-2 group-hover:hidden transition duration-500 ease-in-out">
- <Image src={AddIcon} alt="Add icon" />
- </button>
-
- <div className="hidden group-hover:flex inset-0 transition-opacity duration-500 ease-in-out">
- <TabsList className="p-2">
- <TabsTrigger value="account">Account</TabsTrigger>
- <TabsTrigger value="password">Password</TabsTrigger>
- </TabsList>
- </div>
- </div>
-
- <div className="bg-secondary all-center rounded-full p-2 mt-4 min-w-14 hidden group-hover:block">
- <TabsContent value="account">
- Make changes to your account here.
- </TabsContent>
- <TabsContent value="password">
- Change your password here.
- </TabsContent>
- </div>
- </Tabs>
+ <DynamicIsland />
- <button className="flex shrink-0 duration-200 items-center gap-2 px-2 py-1.5 rounded-xl hover:bg-secondary">
+ <button className="flex shrink-0 duration-200 items-center gap-2 px-5 py-1.5 rounded-xl hover:bg-secondary">
<Image src={ChatIcon} alt="Chat icon" />
Start new chat
</button>
diff --git a/apps/web/app/(dash)/home/page.tsx b/apps/web/app/(dash)/home/page.tsx
index 7a6bb94f..55f2928e 100644
--- a/apps/web/app/(dash)/home/page.tsx
+++ b/apps/web/app/(dash)/home/page.tsx
@@ -2,8 +2,8 @@ import React from "react";
import Menu from "../menu";
import Header from "../header";
import QueryInput from "./queryinput";
-import { homeSearchParamsCache } from "@/app/helpers/lib/searchParams";
-import { getSpaces } from "../actions";
+import { homeSearchParamsCache } from "@/lib/searchParams";
+import { getSpaces } from "@/app/actions/fetchers";
async function Page({
searchParams,
@@ -13,15 +13,20 @@ async function Page({
// TODO: use this to show a welcome page/modal
const { firstTime } = homeSearchParamsCache.parse(searchParams);
- const spaces = await getSpaces();
+ let spaces = await getSpaces();
+
+ if (!spaces.success) {
+ // TODO: handle this error properly.
+ spaces.data = [];
+ }
return (
- <div className="max-w-3xl flex mx-auto w-full flex-col">
+ <div className="max-w-3xl h-full justify-center flex mx-auto w-full flex-col">
{/* all content goes here */}
{/* <div className="">hi {firstTime ? 'first time' : ''}</div> */}
<div className="w-full h-96">
- <QueryInput initialSpaces={spaces} />
+ <QueryInput initialSpaces={spaces.data} />
</div>
</div>
);
diff --git a/apps/web/app/(dash)/home/queryinput.tsx b/apps/web/app/(dash)/home/queryinput.tsx
index d098fda8..d0c27b8d 100644
--- a/apps/web/app/(dash)/home/queryinput.tsx
+++ b/apps/web/app/(dash)/home/queryinput.tsx
@@ -2,10 +2,11 @@
import { ArrowRightIcon } from "@repo/ui/icons";
import Image from "next/image";
-import React, { useState } from "react";
+import React, { useEffect, useMemo, useState } from "react";
import Divider from "@repo/ui/shadcn/divider";
import { MultipleSelector, Option } from "@repo/ui/shadcn/combobox";
import { useRouter } from "next/navigation";
+import { getSpaces } from "@/app/actions/fetchers";
function QueryInput({
initialQuery = "",
@@ -13,7 +14,10 @@ function QueryInput({
disabled = false,
}: {
initialQuery?: string;
- initialSpaces?: { user: string | null; id: number; name: string }[];
+ initialSpaces?: {
+ id: number;
+ name: string;
+ }[];
disabled?: boolean;
}) {
const [q, setQ] = useState(initialQuery);
@@ -41,14 +45,18 @@ function QueryInput({
return newQ;
};
- const options = initialSpaces.map((x) => ({
- label: x.name,
- value: x.id.toString(),
- }));
+ const options = useMemo(
+ () =>
+ initialSpaces.map((x) => ({
+ label: x.name,
+ value: x.id.toString(),
+ })),
+ [initialSpaces],
+ );
return (
<div>
- <div className="bg-secondary rounded-t-[24px] w-full mt-40">
+ <div className="bg-secondary rounded-t-[24px]">
{/* input and action button */}
<form action={async () => push(parseQ())} className="flex gap-4 p-3">
<textarea
@@ -82,6 +90,7 @@ function QueryInput({
{/* selected sources */}
<div className="flex items-center gap-6 p-2 h-auto bg-secondary rounded-b-[24px]">
<MultipleSelector
+ key={options.length}
disabled={disabled}
defaultOptions={options}
onChange={(e) => setSelectedSpaces(e.map((x) => parseInt(x.value)))}
diff --git a/apps/web/app/(dash)/layout.tsx b/apps/web/app/(dash)/layout.tsx
index dffa27fa..3ec8926e 100644
--- a/apps/web/app/(dash)/layout.tsx
+++ b/apps/web/app/(dash)/layout.tsx
@@ -1,22 +1,25 @@
import Header from "./header";
import Menu from "./menu";
-import { ensureAuth } from "./actions";
import { redirect } from "next/navigation";
+import { auth } from "../../server/auth";
+import { Toaster } from "@repo/ui/shadcn/sonner";
async function Layout({ children }: { children: React.ReactNode }) {
- const info = await ensureAuth();
+ const info = await auth();
if (!info) {
return redirect("/signin");
}
return (
- <main className="h-screen flex flex-col p-4 relative">
+ <main className="h-screen flex flex-col p-4 relative ">
<Header />
<Menu />
{children}
+
+ <Toaster />
</main>
);
}
diff --git a/apps/web/app/(dash)/memories/page.tsx b/apps/web/app/(dash)/memories/page.tsx
new file mode 100644
index 00000000..ff746d1d
--- /dev/null
+++ b/apps/web/app/(dash)/memories/page.tsx
@@ -0,0 +1,133 @@
+"use client";
+
+import { getAllUserMemoriesAndSpaces } from "@/app/actions/fetchers";
+import { Space } from "@/app/actions/types";
+import { Content } from "@/server/db/schema";
+import { NextIcon, SearchIcon, UrlIcon } from "@repo/ui/icons";
+import Image from "next/image";
+import React, { useEffect, useState } from "react";
+
+function Page() {
+ const [filter, setFilter] = useState("All");
+ const setFilterfn = (i: string) => setFilter(i);
+
+ const [search, setSearch] = useState("");
+
+ const [memoriesAndSpaces, setMemoriesAndSpaces] = useState<{
+ memories: Content[];
+ spaces: Space[];
+ }>({ memories: [], spaces: [] });
+
+ useEffect(() => {
+ (async () => {
+ const { success, data } = await getAllUserMemoriesAndSpaces();
+ if (!success ?? !data) return;
+ setMemoriesAndSpaces({ memories: data.memories, spaces: data.spaces });
+ })();
+ }, []);
+
+ return (
+ <div className="max-w-3xl min-w-3xl py-36 h-full flex mx-auto w-full flex-col gap-12">
+ <h2 className="text-white w-full font-medium text-2xl text-left">
+ My Memories
+ </h2>
+
+ <div className="flex flex-col gap-4">
+ <div className="w-full relative">
+ <input
+ type="text"
+ className=" w-full py-3 rounded-md text-lg pl-8 bg-[#1F2428] outline-none"
+ placeholder="search here..."
+ />
+ <Image
+ className="absolute top-1/2 -translate-y-1/2 left-2"
+ src={SearchIcon}
+ alt="Search icon"
+ />
+ </div>
+
+ <Filters filter={filter} setFilter={setFilterfn} />
+ </div>
+ <div>
+ <div className="text-[#B3BCC5]">Spaces</div>
+ {memoriesAndSpaces.spaces.map((space) => (
+ <TabComponent title={space.name} description={space.id.toString()} />
+ ))}
+ </div>
+
+ <div>
+ <div className="text-[#B3BCC5]">Pages</div>
+ {memoriesAndSpaces.memories.map((memory) => (
+ <LinkComponent title={memory.title ?? "No title"} url={memory.url} />
+ ))}
+ </div>
+ </div>
+ );
+}
+
+function TabComponent({
+ title,
+ description,
+}: {
+ title: string;
+ description: string;
+}) {
+ return (
+ <div className="flex items-center my-6">
+ <div>
+ <div className="h-12 w-12 bg-[#1F2428] flex justify-center items-center rounded-md">
+ {title.slice(0, 2).toUpperCase()}
+ </div>
+ </div>
+ <div className="grow px-4">
+ <div className="text-lg text-[#fff]">{title}</div>
+ <div>{description}</div>
+ </div>
+ <div>
+ <Image src={NextIcon} alt="Search icon" />
+ </div>
+ </div>
+ );
+}
+
+function LinkComponent({ title, url }: { title: string; url: string }) {
+ return (
+ <div className="flex items-center my-6">
+ <div>
+ <div className="h-12 w-12 bg-[#1F2428] flex justify-center items-center rounded-md">
+ <Image src={UrlIcon} alt="Url icon" />
+ </div>
+ </div>
+ <div className="grow px-4">
+ <div className="text-lg text-[#fff]">{title}</div>
+ <div>{url}</div>
+ </div>
+ </div>
+ );
+}
+
+const FilterMethods = ["All", "Spaces", "Pages", "Notes"];
+function Filters({
+ setFilter,
+ filter,
+}: {
+ setFilter: (i: string) => void;
+ filter: string;
+}) {
+ return (
+ <div className="flex gap-4">
+ {FilterMethods.map((i) => {
+ return (
+ <div
+ onClick={() => setFilter(i)}
+ className={`transition px-6 py-2 rounded-xl ${i === filter ? "bg-[#21303D] text-[#369DFD]" : "text-[#B3BCC5] bg-[#1F2428] hover:bg-[#1f262d] hover:text-[#76a3cc]"}`}
+ >
+ {i}
+ </div>
+ );
+ })}
+ </div>
+ );
+}
+
+export default Page;
diff --git a/apps/web/app/(dash)/menu.tsx b/apps/web/app/(dash)/menu.tsx
index 1177bca6..5f26f545 100644
--- a/apps/web/app/(dash)/menu.tsx
+++ b/apps/web/app/(dash)/menu.tsx
@@ -1,13 +1,14 @@
import React from "react";
import Image from "next/image";
import { MemoriesIcon, ExploreIcon, HistoryIcon } from "@repo/ui/icons";
+import Link from "next/link";
function Menu() {
const menuItems = [
{
icon: MemoriesIcon,
text: "Memories",
- url: "/",
+ url: "/memories",
},
{
icon: ExploreIcon,
@@ -22,23 +23,27 @@ function Menu() {
];
return (
- <div className="absolute h-full p-4 flex items-center top-0 left-0">
+ <div className="fixed h-screen pb-[25vh] w-full p-4 flex items-end justify-end lg:justify-start lg:items-center top-0 left-0 pointer-events-none">
<div className="">
- <div className="hover:rounded-2x group inline-flex w-14 text-foreground-menu text-[15px] font-medium flex-col items-start gap-6 overflow-hidden rounded-[28px] bg-secondary px-3 py-4 duration-200 hover:w-40">
+ <div className="pointer-events-auto group flex w-14 text-foreground-menu text-[15px] font-medium flex-col items-start gap-6 overflow-hidden rounded-[28px] bg-secondary px-3 py-4 duration-200 hover:w-40">
{menuItems.map((item) => (
- <div
+ <Link
+ href={item.url}
key={item.url}
- className="flex w-full cursor-pointer items-center gap-3 px-1 duration-200 hover:scale-105 hover:brightness-150 active:scale-90"
+ className="flex w-full cursor-pointer items-center gap-3 px-1 duration-200 hover:scale-105 hover:brightness-150 active:scale-90 justify-end md:justify-start"
>
+ <p className="md:hidden opacity-0 duration-200 group-hover:opacity-100">
+ {item.text}
+ </p>
<Image
src={item.icon}
alt={`${item.text} icon`}
- className="hover:brightness-125 duration-200"
+ className="hover:brightness-125 duration-200 "
/>
- <p className="opacity-0 duration-200 group-hover:opacity-100">
+ <p className="hidden md:block opacity-0 duration-200 group-hover:opacity-100">
{item.text}
</p>
- </div>
+ </Link>
))}
</div>
</div>
diff --git a/apps/web/app/(editor)/ai.md b/apps/web/app/(editor)/ai.md
new file mode 100644
index 00000000..1528a7bd
--- /dev/null
+++ b/apps/web/app/(editor)/ai.md
@@ -0,0 +1,43 @@
+## to access the editor
+```
+import { useEditor } from "novel";
+const editor = useEditor()
+```
+
+## to get previous text
+```
+import { getPrevText } from "novel/utils";
+const pos = editor.state.selection.from;
+const text = getPrevText(editor, pos);
+```
+
+## selected content into markdown format
+```
+const slice = editor.state.selection.content();
+const text = editor.storage.markdown.serializer.serialize(slice.content);
+```
+
+## replace Selection
+```
+const selection = editor.view.state.selection;
+editor.chain().focus()
+ .insertContentAt(
+ {
+ from: selection.from,
+ to: selection.to,
+ },
+ completion,
+ )
+ .run();
+```
+
+
+## to insert after
+```
+const selection = editor.view.state.selection;
+editor
+ .chain()
+ .focus()
+ .insertContentAt(selection.to + 1, completion)
+ .run();
+```
diff --git a/apps/web/app/(editor)/components/aigenerate.tsx b/apps/web/app/(editor)/components/aigenerate.tsx
new file mode 100644
index 00000000..f27fd50f
--- /dev/null
+++ b/apps/web/app/(editor)/components/aigenerate.tsx
@@ -0,0 +1,169 @@
+import React, { useEffect, useRef, useState } from "react";
+import Magic from "./ui/magic";
+import CrazySpinner from "./ui/crazy-spinner";
+import Asksvg from "./ui/asksvg";
+import Rewritesvg from "./ui/rewritesvg";
+import Translatesvg from "./ui/translatesvg";
+import Autocompletesvg from "./ui/autocompletesvg";
+import { motion, AnimatePresence } from "framer-motion";
+import type { Editor } from "@tiptap/core";
+import { useEditor } from "novel";
+
+function Aigenerate() {
+ const [visible, setVisible] = useState(false);
+ const [generating, setGenerating] = useState(false);
+
+ const { editor } = useEditor();
+ const setGeneratingfn = (v: boolean) => setGenerating(v);
+
+ return (
+ <div className="z-[60] bg-[#171B1F] fixed left-0 bottom-0 w-screen flex justify-center pt-4 pb-6">
+ <motion.div
+ animate={{
+ y: visible ? "30%" : 0,
+ }}
+ onClick={() => {
+ setVisible(!visible);
+ if (visible) editor?.commands.unsetAIHighlight();
+ }}
+ className={`select-none relative z-[70] rounded-3xl text-[#369DFD] bg-[#21303D] px-4 py-3 text-sm flex gap-2 items-center font-medium whitespace-nowrap overflow-hidden transition-[width] w-[6.25rem] ${visible && "w-[10.55rem]"}`}
+ >
+ <Magic className="h-4 w-4 shrink-0 translate-y-[5%]" />
+ {visible && generating ? (
+ <>
+ Generating <CrazySpinner />
+ </>
+ ) : visible ? (
+ <>Press Commands</>
+ ) : (
+ <>Ask AI</>
+ )}
+ </motion.div>
+ <motion.div
+ initial={{
+ opacity: 0,
+ y: 20,
+ }}
+ animate={{
+ y: visible ? "-60%" : 20,
+ opacity: visible ? 1 : 0,
+ }}
+ whileHover={{ scale: 1.05 }}
+ transition={{
+ duration: 0.2,
+ }}
+ className="absolute z-50 top-0"
+ >
+ <ToolBar setGeneratingfn={setGeneratingfn} editor={editor} />
+ <div className="h-8 w-18rem bg-blue-600 blur-[16rem]" />
+ </motion.div>
+ </div>
+ );
+}
+
+export default Aigenerate;
+
+const options = [
+ <><Translatesvg />Translate</>,
+ <><Rewritesvg />Change Tone</>,
+ <><Asksvg />Ask Gemini</>,
+ <><Autocompletesvg />Auto Complete</>
+];
+
+function ToolBar({
+ editor,
+ setGeneratingfn,
+}: {
+ editor: Editor;
+ setGeneratingfn: (v: boolean) => void;
+}) {
+ const [index, setIndex] = useState(0);
+
+ return (
+ <div
+ className={
+ "select-none flex gap-6 bg-[#1F2428] active:scale-[.98] transition rounded-3xl px-1 py-1 text-sm font-medium"
+ }
+ >
+ {options.map((item, idx) => (
+ <div
+ key={idx}
+ className="relative block h-full w-full px-3 py-2 text-[#989EA4]"
+ onMouseEnter={() => setIndex(idx)}
+ >
+ <AnimatePresence>
+ {index === idx && (
+ <motion.span
+ onClick={() =>
+ AigenerateContent({ idx, editor, setGeneratingfn })
+ }
+ className="absolute select-none inset-0 block h-full w-full rounded-xl bg-background-light"
+ layoutId="hoverBackground"
+ initial={{ opacity: 0 }}
+ animate={{
+ opacity: 1,
+ transition: { duration: 0.15 },
+ }}
+ exit={{
+ opacity: 0,
+ transition: { duration: 0.15, delay: 0.2 },
+ }}
+ />
+ )}
+ </AnimatePresence>
+ <div className="select-none flex items-center whitespace-nowrap gap-3 relative z-[60] pointer-events-none">
+ {item}
+ </div>
+ </div>
+ ))}
+ </div>
+ );
+}
+
+async function AigenerateContent({
+ idx,
+ editor,
+ setGeneratingfn,
+}: {
+ idx: number;
+ editor: Editor;
+ setGeneratingfn: (v: boolean) => void;
+}) {
+ setGeneratingfn(true);
+
+ const { from, to } = editor.view.state.selection;
+
+ const slice = editor.state.selection.content();
+ const text = editor.storage.markdown.serializer.serialize(slice.content);
+
+ const request = [
+ "Translate to hindi written in english, do not write anything else",
+ "change tone, improve the way be more formal",
+ "ask, answer the question",
+ "continue this, maximum 30 characters, do not repeat just continue don't use ... to denote start",
+ ]
+
+ const res = await fetch("/api/editorai", {
+ method: "POST",
+ body: JSON.stringify({
+ context: text,
+ request: request[idx],
+ }),
+ })
+ const {completion}: {completion: string} = await res.json();
+ console.log(completion)
+
+ if (idx === 0 || idx === 1){
+ const selectionLength = completion.length + from
+ editor.chain().focus()
+ .insertContentAt({from, to}, completion).setTextSelection({from, to: selectionLength})
+ .run();
+ } else {
+ const selectionLength = completion.length + to + 1
+ editor.chain().focus()
+ .insertContentAt(to+1, completion).setTextSelection({from, to: selectionLength})
+ .run();
+ }
+
+ setGeneratingfn(false);
+}
diff --git a/apps/web/app/(editor)/components/editorcommands.tsx b/apps/web/app/(editor)/components/editorcommands.tsx
new file mode 100644
index 00000000..8e80df46
--- /dev/null
+++ b/apps/web/app/(editor)/components/editorcommands.tsx
@@ -0,0 +1,85 @@
+import React, { useState } from "react";
+import {
+ EditorBubble,
+ EditorCommand,
+ EditorCommandEmpty,
+ EditorCommandItem,
+ EditorCommandList,
+} from "novel";
+import { suggestionItems } from "./slash-command";
+import { Separator } from "@repo/ui/shadcn/separator";
+import { NodeSelector } from "./selectors/node-selector";
+import { LinkSelector } from "./selectors/link-selector";
+import { TextButtons } from "./selectors/text-buttons";
+import { ColorSelector } from "./selectors/color-selector";
+import { BgColorSelector } from "./selectors/bgcolor-selector";
+
+function EditorCommands() {
+ return (
+ <>
+ <SlashCommand />
+ <PopupMenu />
+ </>
+ );
+}
+
+function SlashCommand() {
+ return (
+ <EditorCommand className="z-50 h-auto max-h-[330px] min-w-[20rem] overflow-y-auto rounded-lg bg-[#1F2428] shadow-md transition-all">
+ <EditorCommandEmpty className="px-4 text-lg text-muted-foreground">
+ No results
+ </EditorCommandEmpty>
+ <EditorCommandList>
+ {suggestionItems.map((item) => (
+ <EditorCommandItem
+ value={item.title}
+ onCommand={(val) => item.command(val)}
+ className="flex w-full items-center space-x-4 rounded-md px-4 py-3 text-left text-sm hover:bg-accent aria-selected:bg-[#21303D] group/command"
+ key={item.title}
+ >
+ <div className="flex h-11 w-11 items-center justify-center rounded-md bg-[#2D343A] group-aria-selected/command:bg-[#369DFD33] stroke-[#989EA4] group-aria-selected/command:stroke-[#369DFD]">
+ {item.icon}
+ </div>
+ <div>
+ <p className="font-medium text-[#FFFFFF] group-aria-selected/command:text-[#369DFD]">
+ {item.title}
+ </p>
+ <p className="text-xs text-muted-foreground group-aria-selected/command:text-[#369DFDB2]">
+ {item.description}
+ </p>
+ </div>
+ </EditorCommandItem>
+ ))}
+ </EditorCommandList>
+ </EditorCommand>
+ );
+}
+
+function PopupMenu() {
+ const [openNode, setOpenNode] = useState(false);
+ const [openColor, setOpenColor] = useState(false);
+ const [openBgColor, setOpenBgColor] = useState(false);
+ const [openLink, setOpenLink] = useState(false);
+ const [openMenu, setOpenMenu] = useState(false);
+ return (
+ <EditorBubble
+ tippyOptions={{
+ placement: openMenu ? "bottom-start" : "top",
+ }}
+ className="flex w-fit max-w-[90vw] overflow-hidden bg-[#1F2428] text-white rounded "
+ >
+ <Separator orientation="vertical" />
+ <NodeSelector open={openNode} onOpenChange={setOpenNode} />
+ <Separator orientation="vertical" />
+ <LinkSelector open={openLink} onOpenChange={setOpenLink} />
+ <Separator orientation="vertical" />
+ <TextButtons />
+ <Separator orientation="vertical" />
+ <ColorSelector open={openColor} onOpenChange={setOpenColor} />
+ <Separator orientation="vertical" />
+ <BgColorSelector open={openBgColor} onOpenChange={setOpenBgColor} />
+ </EditorBubble>
+ );
+}
+
+export default EditorCommands;
diff --git a/apps/web/app/(editor)/components/extensions.ts b/apps/web/app/(editor)/components/extensions.ts
new file mode 100644
index 00000000..0c581154
--- /dev/null
+++ b/apps/web/app/(editor)/components/extensions.ts
@@ -0,0 +1,141 @@
+import {
+ AIHighlight,
+ CharacterCount,
+ CodeBlockLowlight,
+ GlobalDragHandle,
+ HorizontalRule,
+ Placeholder,
+ StarterKit,
+ TaskItem,
+ TaskList,
+ TiptapImage,
+ TiptapLink,
+ UpdatedImage,
+ Youtube,
+} from "novel/extensions";
+import { UploadImagesPlugin } from "novel/plugins";
+
+import { cx } from "class-variance-authority";
+import { common, createLowlight } from "lowlight";
+
+//TODO I am using cx here to get tailwind autocomplete working, idk if someone else can write a regex to just capture the class key in objects
+const aiHighlight = AIHighlight;
+//You can overwrite the placeholder with your own configuration
+const placeholder = Placeholder;
+const tiptapLink = TiptapLink.configure({
+ HTMLAttributes: {
+ class: cx(
+ "text-muted-foreground underline underline-offset-[3px] hover:text-primary transition-colors cursor-pointer",
+ ),
+ },
+});
+
+const tiptapImage = TiptapImage.extend({
+ addProseMirrorPlugins() {
+ return [
+ UploadImagesPlugin({
+ imageClass: cx("opacity-40 rounded-lg border border-stone-200"),
+ }),
+ ];
+ },
+}).configure({
+ allowBase64: true,
+ HTMLAttributes: {
+ class: cx("rounded-lg border border-muted"),
+ },
+});
+
+const updatedImage = UpdatedImage.configure({
+ HTMLAttributes: {
+ class: cx("rounded-lg border border-muted"),
+ },
+});
+
+const taskList = TaskList.configure({
+ HTMLAttributes: {
+ class: cx("not-prose pl-2 "),
+ },
+});
+const taskItem = TaskItem.configure({
+ HTMLAttributes: {
+ class: cx("flex gap-2 items-start my-4"),
+ },
+ nested: true,
+});
+
+const horizontalRule = HorizontalRule.configure({
+ HTMLAttributes: {
+ class: cx("mt-4 mb-6 border-t border-muted-foreground"),
+ },
+});
+
+const starterKit = StarterKit.configure({
+ bulletList: {
+ HTMLAttributes: {
+ class: cx("list-disc list-outside leading-3 -mt-2"),
+ },
+ },
+ orderedList: {
+ HTMLAttributes: {
+ class: cx("list-decimal list-outside leading-3 -mt-2"),
+ },
+ },
+ listItem: {
+ HTMLAttributes: {
+ class: cx("leading-normal -mb-2"),
+ },
+ },
+ blockquote: {
+ HTMLAttributes: {
+ class: cx("border-l-4 border-primary"),
+ },
+ },
+ codeBlock: {
+ HTMLAttributes: {
+ class: cx("rounded-md bg-muted text-muted-foreground border p-5 font-mono font-medium"),
+ },
+ },
+ code: {
+ HTMLAttributes: {
+ class: cx("rounded-md bg-muted px-1.5 py-1 font-mono font-medium"),
+ spellcheck: "false",
+ },
+ },
+ horizontalRule: false,
+ dropcursor: {
+ color: "#DBEAFE",
+ width: 4,
+ },
+ gapcursor: false,
+});
+
+const codeBlockLowlight = CodeBlockLowlight.configure({
+ // configure lowlight: common / all / use highlightJS in case there is a need to specify certain language grammars only
+ // common: covers 37 language grammars which should be good enough in most cases
+ lowlight: createLowlight(common),
+});
+
+const youtube = Youtube.configure({
+ HTMLAttributes: {
+ class: cx("rounded-lg border border-muted"),
+ },
+ inline: false,
+});
+
+const characterCount = CharacterCount.configure();
+
+export const defaultExtensions = [
+ starterKit,
+ placeholder,
+ tiptapLink,
+ tiptapImage,
+ updatedImage,
+ taskList,
+ taskItem,
+ horizontalRule,
+ aiHighlight,
+ codeBlockLowlight,
+ youtube,
+ characterCount,
+ GlobalDragHandle,
+];
diff --git a/apps/web/app/(editor)/components/image-upload.ts b/apps/web/app/(editor)/components/image-upload.ts
new file mode 100644
index 00000000..d10be168
--- /dev/null
+++ b/apps/web/app/(editor)/components/image-upload.ts
@@ -0,0 +1,50 @@
+import { createImageUpload } from "novel/plugins";
+import { toast } from "sonner";
+
+const onUpload = (file: File) => {
+ //Endpoint: to upload the image
+ const promise = fetch("", {
+ method: "POST",
+ body: file,
+ });
+
+ return new Promise((resolve, reject) => {
+ toast.promise(
+ promise.then(async (res) => {
+ if (res.status === 200) {
+ const { url } = (await res.json()) as { url: string };
+ const image = new Image();
+ image.src = url;
+ image.onload = () => {
+ resolve(url);
+ };
+ } else {
+ throw new Error("Error uploading image. Please try again.");
+ }
+ }),
+ {
+ loading: "Uploading image...",
+ success: "Image uploaded successfully.",
+ error: (e) => {
+ reject(e);
+ return e.message;
+ },
+ },
+ );
+ });
+};
+
+export const uploadFn = createImageUpload({
+ onUpload,
+ validateFn: (file) => {
+ if (!file.type.includes("image/")) {
+ toast.error("File type not supported.");
+ return false;
+ }
+ if (file.size / 1024 / 1024 > 20) {
+ toast.error("File size too big (max 20MB).");
+ return false;
+ }
+ return true;
+ },
+});
diff --git a/apps/web/app/(editor)/components/selectors/bgcolor-selector.tsx b/apps/web/app/(editor)/components/selectors/bgcolor-selector.tsx
new file mode 100644
index 00000000..77da0f03
--- /dev/null
+++ b/apps/web/app/(editor)/components/selectors/bgcolor-selector.tsx
@@ -0,0 +1,107 @@
+import { Check, ChevronDown } from "lucide-react";
+import { EditorBubbleItem, useEditor } from "novel";
+
+import { Button } from "@repo/ui/shadcn/button";
+import { Popover, PopoverContent, PopoverTrigger } from "@repo/ui/shadcn/popover";
+export interface BubbleColorMenuItem {
+ name: string;
+ color: string;
+}
+
+const HIGHLIGHT_COLORS: BubbleColorMenuItem[] = [
+ {
+ name: "Default",
+ color: "var(--novel-highlight-default)",
+ },
+ {
+ name: "Purple",
+ color: "var(--novel-highlight-purple)",
+ },
+ {
+ name: "Red",
+ color: "var(--novel-highlight-red)",
+ },
+ {
+ name: "Yellow",
+ color: "var(--novel-highlight-yellow)",
+ },
+ {
+ name: "Blue",
+ color: "var(--novel-highlight-blue)",
+ },
+ {
+ name: "Green",
+ color: "var(--novel-highlight-green)",
+ },
+ {
+ name: "Orange",
+ color: "var(--novel-highlight-orange)",
+ },
+ {
+ name: "Pink",
+ color: "var(--novel-highlight-pink)",
+ },
+ {
+ name: "Gray",
+ color: "var(--novel-highlight-gray)",
+ },
+];
+
+interface ColorSelectorProps {
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+}
+
+export const BgColorSelector = ({ open, onOpenChange }: ColorSelectorProps) => {
+ const { editor } = useEditor();
+
+ if (!editor) return null;
+
+ const activeHighlightItem = HIGHLIGHT_COLORS.find(({ color }) => editor.isActive("highlight", { color }));
+
+ return (
+ <Popover modal={true} open={open} onOpenChange={onOpenChange}>
+ <PopoverTrigger asChild>
+ <Button size="sm" className="gap-2 rounded-none" variant="ghost">
+ <span
+ className="rounded-sm px-1"
+ style={{
+ backgroundColor: activeHighlightItem?.color,
+ }}
+ >
+ A
+ </span>
+ <ChevronDown className="h-4 w-4" />
+ </Button>
+ </PopoverTrigger>
+
+ <PopoverContent
+ sideOffset={5}
+ className="my-1 border-none bg-[#1F2428] flex max-h-80 w-48 flex-col overflow-hidden overflow-y-auto rounded border p-1 shadow-xl "
+ align="start"
+ >
+ <div>
+ <div className="my-1 px-2 text-sm font-semibold text-muted-foreground">Background</div>
+ {HIGHLIGHT_COLORS.map(({ name, color }) => (
+ <EditorBubbleItem
+ key={name}
+ onSelect={() => {
+ editor.commands.unsetHighlight();
+ name !== "Default" && editor.commands.setHighlight({ color });
+ }}
+ className="flex cursor-pointer items-center justify-between px-2 py-1 text-sm hover:bg-[#21303D]"
+ >
+ <div className="flex items-center gap-2">
+ <div className="rounded-sm px-2 py-px font-medium" style={{ backgroundColor: color }}>
+ A
+ </div>
+ <span>{name}</span>
+ </div>
+ {editor.isActive("highlight", { color }) && <Check className="h-4 w-4" />}
+ </EditorBubbleItem>
+ ))}
+ </div>
+ </PopoverContent>
+ </Popover>
+ );
+};
diff --git a/apps/web/app/(editor)/components/selectors/color-selector.tsx b/apps/web/app/(editor)/components/selectors/color-selector.tsx
new file mode 100644
index 00000000..557c4255
--- /dev/null
+++ b/apps/web/app/(editor)/components/selectors/color-selector.tsx
@@ -0,0 +1,111 @@
+import { Check, ChevronDown } from "lucide-react";
+import { EditorBubbleItem, useEditor } from "novel";
+
+import { Button } from "@repo/ui/shadcn/button";
+import { Popover, PopoverContent, PopoverTrigger } from "@repo/ui/shadcn/popover";
+export interface BubbleColorMenuItem {
+ name: string;
+ color: string;
+}
+
+const TEXT_COLORS: BubbleColorMenuItem[] = [
+ {
+ name: "Default",
+ color: "var(--novel-black)",
+ },
+ {
+ name: "Purple",
+ color: "#9333EA",
+ },
+ {
+ name: "Red",
+ color: "#E00000",
+ },
+ {
+ name: "Yellow",
+ color: "#EAB308",
+ },
+ {
+ name: "Blue",
+ color: "#2563EB",
+ },
+ {
+ name: "Green",
+ color: "#008A00",
+ },
+ {
+ name: "Orange",
+ color: "#FFA500",
+ },
+ {
+ name: "Pink",
+ color: "#BA4081",
+ },
+ {
+ name: "Gray",
+ color: "#A8A29E",
+ },
+];
+
+
+interface ColorSelectorProps {
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+}
+
+export const ColorSelector = ({ open, onOpenChange }: ColorSelectorProps) => {
+ const { editor } = useEditor();
+
+ if (!editor) return null;
+ const activeColorItem = TEXT_COLORS.find(({ color }) => editor.isActive("textStyle", { color }));
+
+ return (
+ <Popover modal={true} open={open} onOpenChange={onOpenChange}>
+ <PopoverTrigger asChild>
+ <Button size="sm" className="gap-2 rounded-none" variant="ghost">
+ <span
+ className="rounded-sm px-1"
+ style={{
+ color: activeColorItem?.color
+ }}
+ >
+ A
+ </span>
+ <ChevronDown className="h-4 w-4" />
+ </Button>
+ </PopoverTrigger>
+
+ <PopoverContent
+ sideOffset={5}
+ className="my-1 border-none bg-[#1F2428] flex max-h-80 w-48 flex-col overflow-hidden overflow-y-auto rounded border p-1 shadow-xl "
+ align="start"
+ >
+ <div className="flex flex-col">
+ <div className="my-1 px-2 text-sm font-semibold text-muted-foreground">Color</div>
+ {TEXT_COLORS.map(({ name, color }) => (
+ <EditorBubbleItem
+ key={name}
+ onSelect={() => {
+ editor.commands.unsetColor();
+ name !== "Default" &&
+ editor
+ .chain()
+ .focus()
+ .setColor(color || "")
+ .run();
+ }}
+ className="flex cursor-pointer items-center justify-between px-2 py-1 text-sm hover:bg-accent"
+ >
+ <div className="flex items-center gap-2">
+ <div className="rounded-sm px-2 py-px font-medium" style={{ color }}>
+ A
+ </div>
+ <span>{name}</span>
+ </div>
+ </EditorBubbleItem>
+ ))}
+ </div>
+ </PopoverContent>
+ </Popover>
+ );
+};
diff --git a/apps/web/app/(editor)/components/selectors/link-selector.tsx b/apps/web/app/(editor)/components/selectors/link-selector.tsx
new file mode 100644
index 00000000..3dc28266
--- /dev/null
+++ b/apps/web/app/(editor)/components/selectors/link-selector.tsx
@@ -0,0 +1,95 @@
+import { Button } from "@repo/ui/shadcn/button";
+import { Popover, PopoverContent, PopoverTrigger } from "@repo/ui/shadcn/popover";
+import { cn } from "@repo/ui/lib/utils";
+import { Check, Trash } from "lucide-react";
+import { useEditor } from "novel";
+import { useEffect, useRef } from "react";
+
+export function isValidUrl(url: string) {
+ try {
+ new URL(url);
+ return true;
+ } catch (_e) {
+ return false;
+ }
+}
+export function getUrlFromString(str: string) {
+ if (isValidUrl(str)) return str;
+ try {
+ if (str.includes(".") && !str.includes(" ")) {
+ return new URL(`https://${str}`).toString();
+ }
+ } catch (_e) {
+ return null;
+ }
+}
+interface LinkSelectorProps {
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+}
+
+export const LinkSelector = ({ open, onOpenChange }: LinkSelectorProps) => {
+ const inputRef = useRef<HTMLInputElement>(null);
+ const { editor } = useEditor();
+
+ // Autofocus on input by default
+ useEffect(() => {
+ inputRef.current?.focus();
+ });
+ if (!editor) return null;
+
+ return (
+ <Popover modal={true} open={open} onOpenChange={onOpenChange}>
+ <PopoverTrigger asChild>
+ <Button size="sm" variant="ghost" className="gap-2 rounded-none border-none">
+ <p className="text-base">↗</p>
+ <p
+ className={cn("underline decoration-stone-400 underline-offset-4", {
+ "text-blue-500": editor.isActive("link"),
+ })}
+ >
+ Link
+ </p>
+ </Button>
+ </PopoverTrigger>
+ <PopoverContent align="start" className="w-60 p-0" sideOffset={10}>
+ <form
+ onSubmit={(e) => {
+ const target = e.currentTarget as HTMLFormElement;
+ e.preventDefault();
+ const input = target[0] as HTMLInputElement;
+ const url = getUrlFromString(input.value);
+ url && editor.chain().focus().setLink({ href: url }).run();
+ }}
+ className="flex p-1 "
+ >
+ <input
+ ref={inputRef}
+ type="text"
+ placeholder="Paste a link"
+ className="flex-1 bg-background p-1 text-sm outline-none"
+ defaultValue={editor.getAttributes("link").href || ""}
+ />
+ {editor.getAttributes("link").href ? (
+ <Button
+ size="icon"
+ variant="outline"
+ type="button"
+ className="flex h-8 items-center rounded-sm p-1 text-red-600 transition-all hover:bg-red-100 dark:hover:bg-red-800"
+ onClick={() => {
+ editor.chain().focus().unsetLink().run();
+ inputRef.current.value = "";
+ }}
+ >
+ <Trash className="h-4 w-4" />
+ </Button>
+ ) : (
+ <Button size="icon" className="h-8">
+ <Check className="h-4 w-4" />
+ </Button>
+ )}
+ </form>
+ </PopoverContent>
+ </Popover>
+ );
+};
diff --git a/apps/web/app/(editor)/components/selectors/node-selector.tsx b/apps/web/app/(editor)/components/selectors/node-selector.tsx
new file mode 100644
index 00000000..c6092b68
--- /dev/null
+++ b/apps/web/app/(editor)/components/selectors/node-selector.tsx
@@ -0,0 +1,126 @@
+import {
+ Check,
+ CheckSquare,
+ ChevronDown,
+ Code,
+ Heading1,
+ Heading2,
+ Heading3,
+ ListOrdered,
+ type LucideIcon,
+ TextIcon,
+ TextQuote,
+} from "lucide-react";
+import { EditorBubbleItem, useEditor } from "novel";
+
+import { Button } from "@repo/ui/shadcn/button";
+import { Popover, PopoverContent, PopoverTrigger } from "@repo/ui/shadcn/popover";
+
+export type SelectorItem = {
+ name: string;
+ icon: LucideIcon;
+ command: (editor: ReturnType<typeof useEditor>["editor"]) => void;
+ isActive: (editor: ReturnType<typeof useEditor>["editor"]) => boolean;
+};
+
+const items: SelectorItem[] = [
+ {
+ name: "Text",
+ icon: TextIcon,
+ command: (editor) => editor.chain().focus().clearNodes().run(),
+ // I feel like there has to be a more efficient way to do this – feel free to PR if you know how!
+ isActive: (editor) =>
+ editor.isActive("paragraph") && !editor.isActive("bulletList") && !editor.isActive("orderedList"),
+ },
+ {
+ name: "Heading 1",
+ icon: Heading1,
+ command: (editor) => editor.chain().focus().clearNodes().toggleHeading({ level: 1 }).run(),
+ isActive: (editor) => editor.isActive("heading", { level: 1 }),
+ },
+ {
+ name: "Heading 2",
+ icon: Heading2,
+ command: (editor) => editor.chain().focus().clearNodes().toggleHeading({ level: 2 }).run(),
+ isActive: (editor) => editor.isActive("heading", { level: 2 }),
+ },
+ {
+ name: "Heading 3",
+ icon: Heading3,
+ command: (editor) => editor.chain().focus().clearNodes().toggleHeading({ level: 3 }).run(),
+ isActive: (editor) => editor.isActive("heading", { level: 3 }),
+ },
+ {
+ name: "To-do List",
+ icon: CheckSquare,
+ command: (editor) => editor.chain().focus().clearNodes().toggleTaskList().run(),
+ isActive: (editor) => editor.isActive("taskItem"),
+ },
+ {
+ name: "Bullet List",
+ icon: ListOrdered,
+ command: (editor) => editor.chain().focus().clearNodes().toggleBulletList().run(),
+ isActive: (editor) => editor.isActive("bulletList"),
+ },
+ {
+ name: "Numbered List",
+ icon: ListOrdered,
+ command: (editor) => editor.chain().focus().clearNodes().toggleOrderedList().run(),
+ isActive: (editor) => editor.isActive("orderedList"),
+ },
+ {
+ name: "Quote",
+ icon: TextQuote,
+ command: (editor) => editor.chain().focus().clearNodes().toggleBlockquote().run(),
+ isActive: (editor) => editor.isActive("blockquote"),
+ },
+ {
+ name: "Code",
+ icon: Code,
+ command: (editor) => editor.chain().focus().clearNodes().toggleCodeBlock().run(),
+ isActive: (editor) => editor.isActive("codeBlock"),
+ },
+];
+interface NodeSelectorProps {
+ open: boolean;
+ onOpenChange: (open: boolean) => void;
+}
+
+export const NodeSelector = ({ open, onOpenChange }: NodeSelectorProps) => {
+ const { editor } = useEditor();
+ if (!editor) return null;
+ const activeItem = items.filter((item) => item.isActive(editor)).pop() ?? {
+ name: "Multiple",
+ };
+
+ return (
+ <Popover modal={true} open={open} onOpenChange={onOpenChange}>
+ <PopoverTrigger asChild className="gap-2 rounded-none border-none hover:bg-accent focus:ring-0">
+ <Button size="sm" variant="ghost" className="gap-2">
+ <span className="whitespace-nowrap text-sm">{activeItem.name}</span>
+ <ChevronDown className="h-4 w-4" />
+ </Button>
+ </PopoverTrigger>
+ <PopoverContent sideOffset={5} align="start" className="w-48 p-1 border-none bg-[#1F2428]">
+ {items.map((item) => (
+ <EditorBubbleItem
+ key={item.name}
+ onSelect={(editor) => {
+ item.command(editor);
+ onOpenChange(false);
+ }}
+ className="flex cursor-pointer items-center justify-between rounded-sm px-2 py-1 text-sm hover:bg-accent"
+ >
+ <div className="flex items-center space-x-2">
+ <div className="rounded-sm p-1">
+ <item.icon className="h-3 w-3" />
+ </div>
+ <span>{item.name}</span>
+ </div>
+ {activeItem.name === item.name && <Check className="h-4 w-4" />}
+ </EditorBubbleItem>
+ ))}
+ </PopoverContent>
+ </Popover>
+ );
+};
diff --git a/apps/web/app/(editor)/components/selectors/text-buttons.tsx b/apps/web/app/(editor)/components/selectors/text-buttons.tsx
new file mode 100644
index 00000000..f75d7b3c
--- /dev/null
+++ b/apps/web/app/(editor)/components/selectors/text-buttons.tsx
@@ -0,0 +1,63 @@
+import { Button } from "@repo/ui/shadcn/button";
+import { Popover, PopoverContent, PopoverTrigger } from "@repo/ui/shadcn/popover";
+import { BoldIcon, CodeIcon, ItalicIcon, StrikethroughIcon, UnderlineIcon } from "lucide-react";
+import { EditorBubbleItem, useEditor } from "novel";
+import type { SelectorItem } from "./node-selector";
+import { cn } from "@repo/ui/lib/utils";
+
+export const TextButtons = () => {
+ const { editor } = useEditor();
+ if (!editor) return null;
+ const items: SelectorItem[] = [
+ {
+ name: "bold",
+ isActive: (editor) => editor.isActive("bold"),
+ command: (editor) => editor.chain().focus().toggleBold().run(),
+ icon: BoldIcon,
+ },
+ {
+ name: "italic",
+ isActive: (editor) => editor.isActive("italic"),
+ command: (editor) => editor.chain().focus().toggleItalic().run(),
+ icon: ItalicIcon,
+ },
+ {
+ name: "underline",
+ isActive: (editor) => editor.isActive("underline"),
+ command: (editor) => editor.chain().focus().toggleUnderline().run(),
+ icon: UnderlineIcon,
+ },
+ {
+ name: "strike",
+ isActive: (editor) => editor.isActive("strike"),
+ command: (editor) => editor.chain().focus().toggleStrike().run(),
+ icon: StrikethroughIcon,
+ },
+ {
+ name: "code",
+ isActive: (editor) => editor.isActive("code"),
+ command: (editor) => editor.chain().focus().toggleCode().run(),
+ icon: CodeIcon,
+ },
+ ];
+ return (
+ <div className="flex">
+ {items.map((item) => (
+ <EditorBubbleItem
+ key={item.name}
+ onSelect={(editor) => {
+ item.command(editor);
+ }}
+ >
+ <Button size="sm" className="rounded-none" variant="ghost">
+ <item.icon
+ className={cn("h-4 w-4", {
+ "text-blue-500": item.isActive(editor),
+ })}
+ />
+ </Button>
+ </EditorBubbleItem>
+ ))}
+ </div>
+ );
+};
diff --git a/apps/web/app/(editor)/components/slash-command.tsx b/apps/web/app/(editor)/components/slash-command.tsx
new file mode 100644
index 00000000..1bfb1690
--- /dev/null
+++ b/apps/web/app/(editor)/components/slash-command.tsx
@@ -0,0 +1,163 @@
+import {
+ CheckSquare,
+ Code,
+ Heading1,
+ Heading2,
+ Heading3,
+ ImageIcon,
+ List,
+ ListOrdered,
+ MessageSquarePlus,
+ Text,
+ TextQuote,
+ Youtube
+} from "lucide-react";
+import { createSuggestionItems } from "novel/extensions";
+import { Command, renderItems } from "novel/extensions";
+import { uploadFn } from "./image-upload";
+
+export const suggestionItems = createSuggestionItems([
+ {
+ title: "Send Feedback",
+ description: "Let us know how we can improve.",
+ icon: <MessageSquarePlus stroke="inherit" size={18} />,
+ command: ({ editor, range }) => {
+ editor.chain().focus().deleteRange(range).run();
+ window.open("/feedback", "_blank");
+ },
+ },
+ {
+ title: "Text",
+ description: "Just start typing with plain text.",
+ searchTerms: ["p", "paragraph"],
+ icon: <Text stroke="inherit" size={18} />,
+ command: ({ editor, range }) => {
+ editor.chain().focus().deleteRange(range).toggleNode("paragraph", "paragraph").run();
+ },
+ },
+ {
+ title: "To-do List",
+ description: "Track tasks with a to-do list.",
+ searchTerms: ["todo", "task", "list", "check", "checkbox"],
+ icon: <CheckSquare stroke="inherit" size={18} />,
+ command: ({ editor, range }) => {
+ editor.chain().focus().deleteRange(range).toggleTaskList().run();
+ },
+ },
+ {
+ title: "Heading 1",
+ description: "Big section heading.",
+ searchTerms: ["title", "big", "large"],
+ icon: <Heading1 stroke="inherit" size={18} />,
+ command: ({ editor, range }) => {
+ editor.chain().focus().deleteRange(range).setNode("heading", { level: 1 }).run();
+ },
+ },
+ {
+ title: "Heading 2",
+ description: "Medium section heading.",
+ searchTerms: ["subtitle", "medium"],
+ icon: <Heading2 stroke="inherit" size={18} />,
+ command: ({ editor, range }) => {
+ editor.chain().focus().deleteRange(range).setNode("heading", { level: 2 }).run();
+ },
+ },
+ {
+ title: "Heading 3",
+ description: "Small section heading.",
+ searchTerms: ["subtitle", "small"],
+ icon: <Heading3 stroke="inherit" size={18} />,
+ command: ({ editor, range }) => {
+ editor.chain().focus().deleteRange(range).setNode("heading", { level: 3 }).run();
+ },
+ },
+ {
+ title: "Bullet List",
+ description: "Create a simple bullet list.",
+ searchTerms: ["unordered", "point"],
+ icon: <List stroke="inherit" size={18} />,
+ command: ({ editor, range }) => {
+ editor.chain().focus().deleteRange(range).toggleBulletList().run();
+ },
+ },
+ {
+ title: "Numbered List",
+ description: "Create a list with numbering.",
+ searchTerms: ["ordered"],
+ icon: <ListOrdered stroke="inherit" size={18} />,
+ command: ({ editor, range }) => {
+ editor.chain().focus().deleteRange(range).toggleOrderedList().run();
+ },
+ },
+ {
+ title: "Quote",
+ description: "Capture a quote.",
+ searchTerms: ["blockquote"],
+ icon: <TextQuote stroke="inherit" size={18} />,
+ command: ({ editor, range }) =>
+ editor.chain().focus().deleteRange(range).toggleNode("paragraph", "paragraph").toggleBlockquote().run(),
+ },
+ {
+ title: "Code",
+ description: "Capture a code snippet.",
+ searchTerms: ["codeblock"],
+ icon: <Code stroke="inherit" size={18} />,
+ command: ({ editor, range }) => editor.chain().focus().deleteRange(range).toggleCodeBlock().run(),
+ },
+ // {
+ // title: "Image",
+ // description: "Upload an image from your computer.",
+ // searchTerms: ["photo", "picture", "media"],
+ // icon: <ImageIcon stroke="inherit" size={18} />,
+ // command: ({ editor, range }) => {
+ // editor.chain().focus().deleteRange(range).run();
+ // // upload image
+ // const input = document.createElement("input");
+ // input.type = "file";
+ // input.accept = "image/*";
+ // input.onchange = async () => {
+ // if (input.files?.length) {
+ // const file = input.files[0];
+ // const pos = editor.view.state.selection.from;
+ // uploadFn(file, editor.view, pos);
+ // }
+ // };
+ // input.click();
+ // },
+ // },
+ // {
+ // title: "Youtube",
+ // description: "Embed a Youtube video.",
+ // searchTerms: ["video", "youtube", "embed"],
+ // icon: <Youtube stroke="inherit" size={18} />,
+ // command: ({ editor, range }) => {
+ // const videoLink = prompt("Please enter Youtube Video Link");
+ // //From https://regexr.com/3dj5t
+ // const ytregex = new RegExp(
+ // /^((?:https?:)?\/\/)?((?:www|m)\.)?((?:youtube\.com|youtu.be))(\/(?:[\w\-]+\?v=|embed\/|v\/)?)([\w\-]+)(\S+)?$/,
+ // );
+
+ // if (ytregex.test(videoLink)) {
+ // editor
+ // .chain()
+ // .focus()
+ // .deleteRange(range)
+ // .setYoutubeVideo({
+ // src: videoLink,
+ // })
+ // .run();
+ // } else {
+ // if (videoLink !== null) {
+ // alert("Please enter a correct Youtube Video Link");
+ // }
+ // }
+ // },
+ // },
+]);
+
+export const slashCommand = Command.configure({
+ suggestion: {
+ items: () => suggestionItems,
+ render: renderItems,
+ },
+});
diff --git a/apps/web/app/(editor)/components/topbar.tsx b/apps/web/app/(editor)/components/topbar.tsx
new file mode 100644
index 00000000..49b5179c
--- /dev/null
+++ b/apps/web/app/(editor)/components/topbar.tsx
@@ -0,0 +1,68 @@
+"use client";
+
+import {
+ AnimatePresence,
+ useMotionValueEvent,
+ useScroll,
+ motion,
+} from "framer-motion";
+import React, { useState } from "react";
+
+function Topbar({
+ charsCount,
+ saveStatus,
+}: {
+ charsCount: number | undefined;
+ saveStatus: string;
+}) {
+ const [visible, setVisible] = useState(true);
+
+ const { scrollYProgress } = useScroll();
+ useMotionValueEvent(scrollYProgress, "change", (current) => {
+ if (typeof current === "number") {
+ let direction = current! - scrollYProgress.getPrevious()!;
+
+ if (direction < 0 || direction === 1) {
+ setVisible(true);
+ } else {
+ setVisible(false);
+ }
+ }
+ });
+ return (
+ <div className="fixed left-0 top-0 z-10">
+ <AnimatePresence mode="wait">
+ <motion.div
+ initial={{
+ opacity: 1,
+ y: -150,
+ }}
+ animate={{
+ y: visible ? 0 : -150,
+ opacity: visible ? 1 : 0,
+ }}
+ transition={{
+ duration: 0.2,
+ }}
+ className="flex flex-col items-center"
+ >
+ <div className="gap-2 w-screen flex bg-[#171B1F] justify-center items-center pt-6 pb-4">
+ <div className="rounded-lg bg-[#21303D] px-2 py-1 text-sm text-muted-foreground">
+ Untitled
+ </div>
+ <div className="rounded-lg bg-[#21303D] px-2 py-1 text-sm text-muted-foreground">
+ {saveStatus}
+ </div>
+ {charsCount && (
+ <div className="rounded-lg bg-[#21303D] px-2 py-1 text-sm text-muted-foreground">
+ {`${charsCount} words`}
+ </div>
+ )}
+ </div>
+ </motion.div>
+ </AnimatePresence>
+ </div>
+ );
+}
+
+export default Topbar;
diff --git a/apps/web/app/(editor)/components/ui/asksvg.tsx b/apps/web/app/(editor)/components/ui/asksvg.tsx
new file mode 100644
index 00000000..aa38fe08
--- /dev/null
+++ b/apps/web/app/(editor)/components/ui/asksvg.tsx
@@ -0,0 +1,12 @@
+import React from 'react'
+
+function Asksvg() {
+ return (
+ <svg width="15" height="16" viewBox="0 0 15 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M5.90925 4.63925C6.7875 3.8705 8.2125 3.8705 9.09075 4.63925C9.96975 5.408 9.96975 6.6545 9.09075 7.42325C8.9385 7.5575 8.76825 7.66775 8.58825 7.75475C8.0295 8.0255 7.50075 8.504 7.50075 9.125V9.6875M14.25 8C14.25 8.88642 14.0754 9.76417 13.7362 10.5831C13.397 11.4021 12.8998 12.1462 12.273 12.773C11.6462 13.3998 10.9021 13.897 10.0831 14.2362C9.26417 14.5754 8.38642 14.75 7.5 14.75C6.61358 14.75 5.73583 14.5754 4.91689 14.2362C4.09794 13.897 3.35382 13.3998 2.72703 12.773C2.10023 12.1462 1.60303 11.4021 1.26381 10.5831C0.924594 9.76417 0.75 8.88642 0.75 8C0.75 6.20979 1.46116 4.4929 2.72703 3.22703C3.9929 1.96116 5.70979 1.25 7.5 1.25C9.29021 1.25 11.0071 1.96116 12.273 3.22703C13.5388 4.4929 14.25 6.20979 14.25 8ZM7.5 11.9375H7.506V11.9435H7.5V11.9375Z" stroke="#989EA4" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
+
+ )
+}
+
+export default Asksvg \ No newline at end of file
diff --git a/apps/web/app/(editor)/components/ui/autocompletesvg.tsx b/apps/web/app/(editor)/components/ui/autocompletesvg.tsx
new file mode 100644
index 00000000..c433fcad
--- /dev/null
+++ b/apps/web/app/(editor)/components/ui/autocompletesvg.tsx
@@ -0,0 +1,12 @@
+import React from 'react'
+
+function Autocompletesvg() {
+ return (
+ <svg width="15" height="10" viewBox="0 0 15 10" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M1.3125 1.0625H13.6875M1.3125 5H13.6875M1.3125 8.9375H7.5" stroke="#989EA4" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
+
+ )
+}
+
+export default Autocompletesvg \ No newline at end of file
diff --git a/apps/web/app/(editor)/components/ui/crazy-spinner.tsx b/apps/web/app/(editor)/components/ui/crazy-spinner.tsx
new file mode 100644
index 00000000..2e95deee
--- /dev/null
+++ b/apps/web/app/(editor)/components/ui/crazy-spinner.tsx
@@ -0,0 +1,11 @@
+const CrazySpinner = () => {
+ return (
+ <div className="flex justify-center items-center gap-1.5">
+ <div className="h-1.5 w-1.5 animate-ping rounded-full bg-[#369DFD] [animation-delay:-0.4s]" />
+ <div className="h-1.5 w-1.5 animate-ping rounded-full bg-[#369DFD] [animation-delay:-0.2s]" />
+ <div className="h-1.5 w-1.5 animate-ping rounded-full bg-[#369DFD]" />
+ </div>
+ );
+};
+
+export default CrazySpinner;
diff --git a/apps/web/app/(editor)/components/ui/magic.tsx b/apps/web/app/(editor)/components/ui/magic.tsx
new file mode 100644
index 00000000..04dce39e
--- /dev/null
+++ b/apps/web/app/(editor)/components/ui/magic.tsx
@@ -0,0 +1,8 @@
+export default function Magic({ className }: { className: string }) {
+ return (
+<svg width="18" height="19" className={className} viewBox="0 0 18 19" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M6.49998 3.25C6.63578 3.25003 6.76788 3.29429 6.87629 3.37608C6.98469 3.45788 7.06351 3.57275 7.10081 3.70333L7.77831 6.075C7.92418 6.58576 8.19785 7.05092 8.57345 7.42652C8.94906 7.80213 9.41421 8.07579 9.92498 8.22167L12.2966 8.89917C12.4271 8.93655 12.5419 9.0154 12.6236 9.1238C12.7053 9.2322 12.7495 9.36425 12.7495 9.5C12.7495 9.63574 12.7053 9.7678 12.6236 9.8762C12.5419 9.9846 12.4271 10.0634 12.2966 10.1008L9.92498 10.7783C9.41421 10.9242 8.94906 11.1979 8.57345 11.5735C8.19785 11.9491 7.92418 12.4142 7.77831 12.925L7.10081 15.2967C7.06343 15.4272 6.98458 15.5419 6.87618 15.6236C6.76778 15.7054 6.63572 15.7495 6.49998 15.7495C6.36423 15.7495 6.23218 15.7054 6.12378 15.6236C6.01538 15.5419 5.93653 15.4272 5.89914 15.2967L5.22164 12.925C5.07577 12.4142 4.80211 11.9491 4.4265 11.5735C4.05089 11.1979 3.58574 10.9242 3.07498 10.7783L0.70331 10.1008C0.572814 10.0634 0.458036 9.9846 0.376329 9.8762C0.294622 9.7678 0.250427 9.63574 0.250427 9.5C0.250427 9.36425 0.294622 9.2322 0.376329 9.1238C0.458036 9.0154 0.572814 8.93655 0.70331 8.89917L3.07498 8.22167C3.58574 8.07579 4.05089 7.80213 4.4265 7.42652C4.80211 7.05092 5.07577 6.58576 5.22164 6.075L5.89914 3.70333C5.93644 3.57275 6.01526 3.45788 6.12367 3.37608C6.23208 3.29429 6.36417 3.25003 6.49998 3.25ZM14 0.75C14.1394 0.749922 14.2749 0.79647 14.3848 0.882239C14.4947 0.968007 14.5728 1.08807 14.6066 1.22333L14.8216 2.08667C15.0183 2.87 15.63 3.48167 16.4133 3.67833L17.2766 3.89333C17.4122 3.9269 17.5325 4.00488 17.6186 4.11483C17.7046 4.22479 17.7514 4.36038 17.7514 4.5C17.7514 4.63962 17.7046 4.77521 17.6186 4.88517C17.5325 4.99512 17.4122 5.0731 17.2766 5.10667L16.4133 5.32167C15.63 5.51833 15.0183 6.13 14.8216 6.91333L14.6066 7.77667C14.5731 7.91219 14.4951 8.03257 14.3851 8.11861C14.2752 8.20465 14.1396 8.2514 14 8.2514C13.8604 8.2514 13.7248 8.20465 13.6148 8.11861C13.5049 8.03257 13.4269 7.91219 13.3933 7.77667L13.1783 6.91333C13.0822 6.52869 12.8833 6.17741 12.6029 5.89706C12.3226 5.61671 11.9713 5.41782 11.5866 5.32167L10.7233 5.10667C10.5878 5.0731 10.4674 4.99512 10.3814 4.88517C10.2953 4.77521 10.2486 4.63962 10.2486 4.5C10.2486 4.36038 10.2953 4.22479 10.3814 4.11483C10.4674 4.00488 10.5878 3.9269 10.7233 3.89333L11.5866 3.67833C11.9713 3.58218 12.3226 3.38329 12.6029 3.10294C12.8833 2.82258 13.0822 2.47131 13.1783 2.08667L13.3933 1.22333C13.4271 1.08807 13.5052 0.968007 13.6152 0.882239C13.7251 0.79647 13.8605 0.749922 14 0.75ZM12.75 12C12.8812 11.9999 13.0092 12.0412 13.1157 12.1179C13.2222 12.1946 13.3018 12.303 13.3433 12.4275L13.6716 13.4133C13.7966 13.7858 14.0883 14.0792 14.4616 14.2033L15.4475 14.5325C15.5716 14.5742 15.6795 14.6538 15.756 14.7601C15.8324 14.8664 15.8736 14.9941 15.8736 15.125C15.8736 15.2559 15.8324 15.3836 15.756 15.4899C15.6795 15.5962 15.5716 15.6758 15.4475 15.7175L14.4616 16.0467C14.0891 16.1717 13.7958 16.4633 13.6716 16.8367L13.3425 17.8225C13.3008 17.9466 13.2212 18.0545 13.1149 18.131C13.0086 18.2075 12.8809 18.2486 12.75 18.2486C12.619 18.2486 12.4914 18.2075 12.3851 18.131C12.2788 18.0545 12.1992 17.9466 12.1575 17.8225L11.8283 16.8367C11.7669 16.6527 11.6636 16.4856 11.5265 16.3485C11.3894 16.2114 11.2222 16.1081 11.0383 16.0467L10.0525 15.7175C9.92834 15.6758 9.82043 15.5962 9.74398 15.4899C9.66752 15.3836 9.6264 15.2559 9.6264 15.125C9.6264 14.9941 9.66752 14.8664 9.74398 14.7601C9.82043 14.6538 9.92834 14.5742 10.0525 14.5325L11.0383 14.2033C11.4108 14.0783 11.7041 13.7867 11.8283 13.4133L12.1575 12.4275C12.1989 12.3031 12.2784 12.1949 12.3848 12.1182C12.4911 12.0414 12.6189 12.0001 12.75 12Z" fill="#369DFD"/>
+</svg>
+
+ );
+}
diff --git a/apps/web/app/(editor)/components/ui/rewritesvg.tsx b/apps/web/app/(editor)/components/ui/rewritesvg.tsx
new file mode 100644
index 00000000..fad9eb90
--- /dev/null
+++ b/apps/web/app/(editor)/components/ui/rewritesvg.tsx
@@ -0,0 +1,11 @@
+import React from 'react'
+
+function Rewritesvg() {
+ return (
+ <svg width="17" height="14" viewBox="0 0 17 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M11.5172 5.01108H15.2612L12.8755 2.62383C12.1074 1.85573 11.1506 1.30337 10.1014 1.02228C9.05214 0.74119 7.94738 0.741272 6.89818 1.02252C5.84897 1.30377 4.8923 1.85627 4.12433 2.62448C3.35635 3.39269 2.80415 4.34954 2.52323 5.39883M1.73873 12.7331V8.98908M1.73873 8.98908H5.48273M1.73873 8.98908L4.12373 11.3763C4.89181 12.1444 5.84857 12.6968 6.89782 12.9779C7.94706 13.259 9.05182 13.2589 10.101 12.9776C11.1502 12.6964 12.1069 12.1439 12.8749 11.3757C13.6428 10.6075 14.1951 9.65062 14.476 8.60133M15.2612 1.26708V5.00958" stroke="#989EA4" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
+ )
+}
+
+export default Rewritesvg \ No newline at end of file
diff --git a/apps/web/app/(editor)/components/ui/translatesvg.tsx b/apps/web/app/(editor)/components/ui/translatesvg.tsx
new file mode 100644
index 00000000..cde82da6
--- /dev/null
+++ b/apps/web/app/(editor)/components/ui/translatesvg.tsx
@@ -0,0 +1,12 @@
+import React from 'react'
+
+function Translatesvg() {
+ return (
+ <svg width="15" height="16" viewBox="0 0 15 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M6.375 14.75L10.3125 6.3125L14.25 14.75M7.5 12.5H13.125M0.75 3.21575C2.2428 3.02999 3.74569 2.93706 5.25 2.9375M5.25 2.9375C6.09 2.9375 6.92475 2.966 7.7505 3.023M5.25 2.9375V1.25M7.7505 3.023C6.882 6.9935 4.2675 10.31 0.75 12.1265M7.7505 3.023C8.4225 3.06875 9.08925 3.13325 9.75 3.21575M6.30825 9.587C5.07822 8.33647 4.10335 6.85849 3.438 5.2355" stroke="#989EA4" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
+
+ )
+}
+
+export default Translatesvg \ No newline at end of file
diff --git a/apps/web/app/(editor)/editor.tsx b/apps/web/app/(editor)/editor.tsx
new file mode 100644
index 00000000..5b4a60ce
--- /dev/null
+++ b/apps/web/app/(editor)/editor.tsx
@@ -0,0 +1,54 @@
+"use client";
+import { defaultEditorContent } from "./lib/content";
+import { EditorContent, EditorRoot, type JSONContent } from "novel";
+import { ImageResizer } from "novel/extensions";
+import { useEffect, useState } from "react";
+import { defaultExtensions } from "./components/extensions";
+
+import { slashCommand } from "./components/slash-command";
+import { Updates } from "./lib/debouncedsave";
+import { editorProps } from "./lib/editorprops";
+import EditorCommands from "./components/editorcommands";
+import Aigenerate from "./components/aigenerate";
+import { useMotionValueEvent, useScroll } from "framer-motion";
+import Topbar from "./components/topbar";
+
+const Editor = () => {
+ const [initialContent, setInitialContent] = useState<null | JSONContent>(
+ null
+ );
+ const [saveStatus, setSaveStatus] = useState("Saved");
+ const [charsCount, setCharsCount] = useState();
+ const [visible, setVisible] = useState(true);
+
+ useEffect(() => {
+ const content = window.localStorage.getItem("novel-content");
+ if (content) setInitialContent(JSON.parse(content));
+ else setInitialContent(defaultEditorContent);
+ }, []);
+
+ if (!initialContent) return null;
+
+ return (
+ <div className="relative w-full max-w-screen-xl">
+ <Topbar charsCount={charsCount} saveStatus={saveStatus} />
+ <EditorRoot>
+ <EditorContent
+ initialContent={initialContent}
+ extensions={[...defaultExtensions, slashCommand]}
+ className="min-h-[55vh] mt-[8vh] w-full max-w-screen-xl bg-[#171B1F] mb-[40vh]"
+ editorProps={editorProps}
+ onUpdate={({ editor }) => {
+ Updates({ editor, setCharsCount, setSaveStatus });
+ }}
+ slotAfter={<ImageResizer />}
+ >
+ <EditorCommands />
+ <Aigenerate />
+ </EditorContent>
+ </EditorRoot>
+ </div>
+ );
+};
+
+export default Editor;
diff --git a/apps/web/app/(editor)/editor/page.tsx b/apps/web/app/(editor)/editor/page.tsx
new file mode 100644
index 00000000..d0298065
--- /dev/null
+++ b/apps/web/app/(editor)/editor/page.tsx
@@ -0,0 +1,8 @@
+import TailwindAdvancedEditor from "../editor";
+export default function Page() {
+ return (
+ <div className="flex min-h-screen flex-col items-center bg-[#171B1F]">
+ <TailwindAdvancedEditor />
+ </div>
+ );
+}
diff --git a/apps/web/app/(editor)/layout.tsx b/apps/web/app/(editor)/layout.tsx
new file mode 100644
index 00000000..1bf97715
--- /dev/null
+++ b/apps/web/app/(editor)/layout.tsx
@@ -0,0 +1,12 @@
+import "./styles/prosemirror.css";
+import "./styles/globals.css"
+import type { ReactNode } from "react";
+
+
+export default function RootLayout({ children }: { children: ReactNode }) {
+ return (
+ <div className="dark">
+ {children}
+ </div>
+ );
+}
diff --git a/apps/web/app/(editor)/lib/content.ts b/apps/web/app/(editor)/lib/content.ts
new file mode 100644
index 00000000..6464cfa1
--- /dev/null
+++ b/apps/web/app/(editor)/lib/content.ts
@@ -0,0 +1,231 @@
+export const defaultEditorContent = {
+ type: "doc",
+ content: [
+ {
+ type: "heading",
+ attrs: { level: 2 },
+ content: [{ type: "text", text: "Introducing Novel" }],
+ },
+ {
+ type: "paragraph",
+ content: [
+ {
+ type: "text",
+ marks: [
+ {
+ type: "link",
+ attrs: {
+ href: "https://github.com/steven-tey/novel",
+ target: "_blank",
+ },
+ },
+ ],
+ text: "Novel",
+ },
+ {
+ type: "text",
+ text: " is a Notion-style WYSIWYG editor with AI-powered autocompletion. Built with ",
+ },
+ {
+ type: "text",
+ marks: [
+ {
+ type: "link",
+ attrs: {
+ href: "https://tiptap.dev/",
+ target: "_blank",
+ },
+ },
+ ],
+ text: "Tiptap",
+ },
+ { type: "text", text: " + " },
+ {
+ type: "text",
+ marks: [
+ {
+ type: "link",
+ attrs: {
+ href: "https://sdk.vercel.ai/docs",
+ target: "_blank",
+ },
+ },
+ ],
+ text: "Vercel AI SDK",
+ },
+ { type: "text", text: "." },
+ ],
+ },
+ {
+ type: "heading",
+ attrs: { level: 3 },
+ content: [{ type: "text", text: "Installation" }],
+ },
+ {
+ type: "codeBlock",
+ attrs: { language: null },
+ content: [{ type: "text", text: "npm i novel" }],
+ },
+ {
+ type: "heading",
+ attrs: { level: 3 },
+ content: [{ type: "text", text: "Usage" }],
+ },
+ {
+ type: "codeBlock",
+ attrs: { language: null },
+ content: [
+ {
+ type: "text",
+ text: 'import { Editor } from "novel";\n\nexport default function App() {\n return (\n <Editor />\n )\n}',
+ },
+ ],
+ },
+ {
+ type: "heading",
+ attrs: { level: 3 },
+ content: [{ type: "text", text: "Features" }],
+ },
+ {
+ type: "orderedList",
+ attrs: { tight: true, start: 1 },
+ content: [
+ {
+ type: "listItem",
+ content: [
+ {
+ type: "paragraph",
+ content: [{ type: "text", text: "Slash menu & bubble menu" }],
+ },
+ ],
+ },
+ {
+ type: "listItem",
+ content: [
+ {
+ type: "paragraph",
+ content: [
+ { type: "text", text: "AI autocomplete (type " },
+ { type: "text", marks: [{ type: "code" }], text: "++" },
+ {
+ type: "text",
+ text: " to activate, or select from slash menu)",
+ },
+ ],
+ },
+ ],
+ },
+ {
+ type: "listItem",
+ content: [
+ {
+ type: "paragraph",
+ content: [
+ {
+ type: "text",
+ text: "Image uploads (drag & drop / copy & paste, or select from slash menu) ",
+ },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ {
+ type: "image",
+ attrs: {
+ src: "https://public.blob.vercel-storage.com/pJrjXbdONOnAeZAZ/banner-2wQk82qTwyVgvlhTW21GIkWgqPGD2C.png",
+ alt: "banner.png",
+ title: "banner.png",
+ width: null,
+ height: null,
+ },
+ },
+ { type: "horizontalRule" },
+ {
+ type: "heading",
+ attrs: { level: 3 },
+ content: [{ type: "text", text: "Learn more" }],
+ },
+ {
+ type: "taskList",
+ content: [
+ {
+ type: "taskItem",
+ attrs: { checked: false },
+ content: [
+ {
+ type: "paragraph",
+ content: [
+ { type: "text", text: "Star us on " },
+ {
+ type: "text",
+ marks: [
+ {
+ type: "link",
+ attrs: {
+ href: "https://github.com/steven-tey/novel",
+ target: "_blank",
+ },
+ },
+ ],
+ text: "GitHub",
+ },
+ ],
+ },
+ ],
+ },
+ {
+ type: "taskItem",
+ attrs: { checked: false },
+ content: [
+ {
+ type: "paragraph",
+ content: [
+ { type: "text", text: "Install the " },
+ {
+ type: "text",
+ marks: [
+ {
+ type: "link",
+ attrs: {
+ href: "https://www.npmjs.com/package/novel",
+ target: "_blank",
+ },
+ },
+ ],
+ text: "NPM package",
+ },
+ ],
+ },
+ ],
+ },
+ {
+ type: "taskItem",
+ attrs: { checked: false },
+ content: [
+ {
+ type: "paragraph",
+ content: [
+ {
+ type: "text",
+ marks: [
+ {
+ type: "link",
+ attrs: {
+ href: "https://vercel.com/templates/next.js/novel",
+ target: "_blank",
+ },
+ },
+ ],
+ text: "Deploy your own",
+ },
+ { type: "text", text: " to Vercel" },
+ ],
+ },
+ ],
+ },
+ ],
+ },
+ ],
+};
diff --git a/apps/web/app/(editor)/lib/debouncedsave.ts b/apps/web/app/(editor)/lib/debouncedsave.ts
new file mode 100644
index 00000000..6490c6c4
--- /dev/null
+++ b/apps/web/app/(editor)/lib/debouncedsave.ts
@@ -0,0 +1,20 @@
+import hljs from 'highlight.js'
+import { debounce } from 'tldraw';
+import { useDebouncedCallback } from "use-debounce";
+
+export const Updates = debounce(({editor, setCharsCount, setSaveStatus})=> {
+ const json = editor.getJSON();
+ setCharsCount(editor.storage.characterCount.words());
+ window.localStorage.setItem("html-content", highlightCodeblocks(editor.getHTML()));
+ window.localStorage.setItem("novel-content", JSON.stringify(json));
+ window.localStorage.setItem("markdown", editor.storage.markdown.getMarkdown());
+ setSaveStatus("Saved");
+}, 500)
+
+export const highlightCodeblocks = (content: string) => {
+ const doc = new DOMParser().parseFromString(content, 'text/html');
+ doc.querySelectorAll('pre code').forEach((el) => {
+ hljs.highlightElement(el);
+ });
+ return new XMLSerializer().serializeToString(doc);
+}; \ No newline at end of file
diff --git a/apps/web/app/(editor)/lib/editorprops.ts b/apps/web/app/(editor)/lib/editorprops.ts
new file mode 100644
index 00000000..00d89264
--- /dev/null
+++ b/apps/web/app/(editor)/lib/editorprops.ts
@@ -0,0 +1,16 @@
+import { handleCommandNavigation } from "novel/extensions";
+import { handleImageDrop, handleImagePaste } from "novel/plugins";
+import { uploadFn } from "../components/image-upload";
+import { EditorView } from "prosemirror-view";
+
+export const editorProps = {
+ handleDOMEvents: {
+ keydown: (_view: EditorView, event: KeyboardEvent) => handleCommandNavigation(event),
+ },
+ handlePaste: (view: EditorView, event: ClipboardEvent) => handleImagePaste(view, event, uploadFn),
+ handleDrop: (view: EditorView, event: DragEvent, slice, moved:boolean) => handleImageDrop(view, event, moved, uploadFn),
+ attributes: {
+ class:
+ "prose prose-lg dark:prose-invert prose-headings:font-title font-default focus:outline-none max-w-full",
+ },
+} \ No newline at end of file
diff --git a/apps/web/app/(editor)/lib/use-local-storage.ts b/apps/web/app/(editor)/lib/use-local-storage.ts
new file mode 100644
index 00000000..5f2ebeb9
--- /dev/null
+++ b/apps/web/app/(editor)/lib/use-local-storage.ts
@@ -0,0 +1,27 @@
+import { useEffect, useState } from "react";
+
+const useLocalStorage = <T>(
+ key: string,
+ initialValue: T,
+ // eslint-disable-next-line no-unused-vars
+): [T, (value: T) => void] => {
+ const [storedValue, setStoredValue] = useState(initialValue);
+
+ useEffect(() => {
+ // Retrieve from localStorage
+ const item = window.localStorage.getItem(key);
+ if (item) {
+ setStoredValue(JSON.parse(item));
+ }
+ }, [key]);
+
+ const setValue = (value: T) => {
+ // Save state
+ setStoredValue(value);
+ // Save to localStorage
+ window.localStorage.setItem(key, JSON.stringify(value));
+ };
+ return [storedValue, setValue];
+};
+
+export default useLocalStorage;
diff --git a/apps/web/app/(editor)/styles/globals.css b/apps/web/app/(editor)/styles/globals.css
new file mode 100644
index 00000000..336e2dae
--- /dev/null
+++ b/apps/web/app/(editor)/styles/globals.css
@@ -0,0 +1,168 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+ :root {
+ --background: 0 0% 100%;
+ --foreground: 222.2 84% 4.9%;
+
+ --card: 0 0% 100%;
+ --card-foreground: 222.2 84% 4.9%;
+
+ --popover: 0 0% 100%;
+ --popover-foreground: 222.2 84% 4.9%;
+
+ --primary: 222.2 47.4% 11.2%;
+ --primary-foreground: 210 40% 98%;
+
+ --secondary: 210 40% 96.1%;
+ --secondary-foreground: 222.2 47.4% 11.2%;
+
+ --muted: 210 40% 96.1%;
+ --muted-foreground: 215.4 16.3% 46.9%;
+
+ --accent: 210 40% 96.1%;
+ --accent-foreground: 222.2 47.4% 11.2%;
+
+ --destructive: 0 84.2% 60.2%;
+ --destructive-foreground: 210 40% 98%;
+
+ --border: 214.3 31.8% 91.4%;
+ --input: 214.3 31.8% 91.4%;
+ --ring: 222.2 84% 4.9%;
+
+ --radius: 0.5rem;
+
+ --novel-highlight-default: #ffffff;
+ --novel-highlight-purple: #f6f3f8;
+ --novel-highlight-red: #fdebeb;
+ --novel-highlight-yellow: #fbf4a2;
+ --novel-highlight-blue: #c1ecf9;
+ --novel-highlight-green: #acf79f;
+ --novel-highlight-orange: #faebdd;
+ --novel-highlight-pink: #faf1f5;
+ --novel-highlight-gray: #f1f1ef;
+ }
+
+ .dark {
+ --background: #171B1F;
+ --foreground: 210 40% 98%;
+
+ --card: 222.2 84% 4.9%;
+ --card-foreground: 210 40% 98%;
+
+ --popover: 222.2 84% 4.9%;
+ --popover-foreground: 210 40% 98%;
+
+ --primary: 210 40% 98%;
+ --primary-foreground: 222.2 47.4% 11.2%;
+
+ --secondary: 217.2 32.6% 17.5%;
+ --secondary-foreground: 210 40% 98%;
+
+ --muted: 217.2 32.6% 17.5%;
+ --muted-foreground: 215 20.2% 65.1%;
+
+ --accent: 217.2 32.6% 17.5%;
+ --accent-foreground: 210 40% 98%;
+
+ --destructive: 0 62.8% 30.6%;
+ --destructive-foreground: 210 40% 98%;
+
+ --border: 217.2 32.6% 17.5%;
+ --input: 217.2 32.6% 17.5%;
+ --ring: 212.7 26.8% 83.9%;
+
+ --novel-highlight-default: #000000;
+ --novel-highlight-purple: #3f2c4b;
+ --novel-highlight-red: #5c1a1a;
+ --novel-highlight-yellow: #5c4b1a;
+ --novel-highlight-blue: #1a3d5c;
+ --novel-highlight-green: #1a5c20;
+ --novel-highlight-orange: #5c3a1a;
+ --novel-highlight-pink: #5c1a3a;
+ --novel-highlight-gray: #3a3a3a;
+ }
+}
+
+@layer base {
+ * {
+ @apply border-border;
+ }
+ body {
+ @apply bg-background text-foreground;
+ }
+}
+
+
+pre {
+ background: #0d0d0d;
+ border-radius: 0.5rem;
+ color: #fff;
+ font-family: "JetBrainsMono", monospace;
+ padding: 0.75rem 1rem;
+
+ code {
+ background: none;
+ color: inherit;
+ font-size: 0.8rem;
+ padding: 0;
+ }
+
+ .hljs-comment,
+ .hljs-quote {
+ color: #616161;
+ }
+
+ .hljs-variable,
+ .hljs-template-variable,
+ .hljs-attribute,
+ .hljs-tag,
+ .hljs-name,
+ .hljs-regexp,
+ .hljs-link,
+ .hljs-name,
+ .hljs-selector-id,
+ .hljs-selector-class {
+ color: #f98181;
+ }
+
+ .hljs-number,
+ .hljs-meta,
+ .hljs-built_in,
+ .hljs-builtin-name,
+ .hljs-literal,
+ .hljs-type,
+ .hljs-params {
+ color: #fbbc88;
+ }
+
+ .hljs-string,
+ .hljs-symbol,
+ .hljs-bullet {
+ color: #b9f18d;
+ }
+
+ .hljs-title,
+ .hljs-section {
+ color: #faf594;
+ }
+
+ .hljs-keyword,
+ .hljs-selector-tag {
+ color: #70cff8;
+ }
+
+ .hljs-emphasis {
+ font-style: italic;
+ }
+
+ .hljs-strong {
+ font-weight: 700;
+ }
+}
+::-webkit-scrollbar{
+ width: 0;
+}
+
diff --git a/apps/web/app/(editor)/styles/prosemirror.css b/apps/web/app/(editor)/styles/prosemirror.css
new file mode 100644
index 00000000..7298c98b
--- /dev/null
+++ b/apps/web/app/(editor)/styles/prosemirror.css
@@ -0,0 +1,203 @@
+.ProseMirror {
+ @apply p-12 px-8 sm:px-12;
+}
+
+.ProseMirror .is-editor-empty:first-child::before {
+ content: attr(data-placeholder);
+ float: left;
+ color: hsl(var(--muted-foreground));
+ pointer-events: none;
+ height: 0;
+}
+.ProseMirror .is-empty::before {
+ content: attr(data-placeholder);
+ float: left;
+ color: hsl(var(--muted-foreground));
+ pointer-events: none;
+ height: 0;
+}
+
+/* Custom image styles */
+
+.ProseMirror img {
+ transition: filter 0.1s ease-in-out;
+
+ &:hover {
+ cursor: pointer;
+ filter: brightness(90%);
+ }
+
+ &.ProseMirror-selectednode {
+ outline: 3px solid #5abbf7;
+ filter: brightness(90%);
+ }
+}
+
+.img-placeholder {
+ position: relative;
+
+ &:before {
+ content: "";
+ box-sizing: border-box;
+ position: absolute;
+ top: 50%;
+ left: 50%;
+ width: 36px;
+ height: 36px;
+ border-radius: 50%;
+ border: 3px solid var(--novel-stone-200);
+ border-top-color: var(--novel-stone-800);
+ animation: spinning 0.6s linear infinite;
+ }
+}
+
+@keyframes spinning {
+ to {
+ transform: rotate(360deg);
+ }
+}
+
+/* Custom TODO list checkboxes – shoutout to this awesome tutorial: https://moderncss.dev/pure-css-custom-checkbox-style/ */
+
+ul[data-type="taskList"] li > label {
+ margin-right: 0.2rem;
+ user-select: none;
+}
+
+@media screen and (max-width: 768px) {
+ ul[data-type="taskList"] li > label {
+ margin-right: 0.5rem;
+ }
+}
+
+ul[data-type="taskList"] li > label input[type="checkbox"] {
+ -webkit-appearance: none;
+ appearance: none;
+ background-color: hsl(var(--background));
+ margin: 0;
+ cursor: pointer;
+ width: 1.2em;
+ height: 1.2em;
+ position: relative;
+ top: 5px;
+ border: 2px solid hsl(var(--border));
+ margin-right: 0.3rem;
+ display: grid;
+ place-content: center;
+
+ &:hover {
+ background-color: hsl(var(--accent));
+ }
+
+ &:active {
+ background-color: hsl(var(--accent));
+ }
+
+ &::before {
+ content: "";
+ width: 0.65em;
+ height: 0.65em;
+ transform: scale(0);
+ transition: 120ms transform ease-in-out;
+ box-shadow: inset 1em 1em;
+ transform-origin: center;
+ clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
+ }
+
+ &:checked::before {
+ transform: scale(1);
+ }
+}
+
+ul[data-type="taskList"] li[data-checked="true"] > div > p {
+ color: var(--muted-foreground);
+ text-decoration: line-through;
+ text-decoration-thickness: 2px;
+}
+
+/* Overwrite tippy-box original max-width */
+
+.tippy-box {
+ max-width: 400px !important;
+}
+
+.ProseMirror:not(.dragging) .ProseMirror-selectednode {
+ outline: none !important;
+ background-color: var(--novel-highlight-blue);
+ transition: background-color 0.2s;
+ box-shadow: none;
+}
+
+.drag-handle {
+ position: fixed;
+ opacity: 1;
+ transition: opacity ease-in 0.2s;
+ border-radius: 0.25rem;
+
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10' style='fill: rgba(0, 0, 0, 0.5)'%3E%3Cpath d='M3,2 C2.44771525,2 2,1.55228475 2,1 C2,0.44771525 2.44771525,0 3,0 C3.55228475,0 4,0.44771525 4,1 C4,1.55228475 3.55228475,2 3,2 Z M3,6 C2.44771525,6 2,5.55228475 2,5 C2,4.44771525 2.44771525,4 3,4 C3.55228475,4 4,4.44771525 4,5 C4,5.55228475 3.55228475,6 3,6 Z M3,10 C2.44771525,10 2,9.55228475 2,9 C2,8.44771525 2.44771525,8 3,8 C3.55228475,8 4,8.44771525 4,9 C4,9.55228475 3.55228475,10 3,10 Z M7,2 C6.44771525,2 6,1.55228475 6,1 C6,0.44771525 6.44771525,0 7,0 C7.55228475,0 8,0.44771525 8,1 C8,1.55228475 7.55228475,2 7,2 Z M7,6 C6.44771525,6 6,5.55228475 6,5 C6,4.44771525 6.44771525,4 7,4 C7.55228475,4 8,4.44771525 8,5 C8,5.55228475 7.55228475,6 7,6 Z M7,10 C6.44771525,10 6,9.55228475 6,9 C6,8.44771525 6.44771525,8 7,8 C7.55228475,8 8,8.44771525 8,9 C8,9.55228475 7.55228475,10 7,10 Z'%3E%3C/path%3E%3C/svg%3E");
+ background-size: calc(0.5em + 0.375rem) calc(0.5em + 0.375rem);
+ background-repeat: no-repeat;
+ background-position: center;
+ width: 1.2rem;
+ height: 1.5rem;
+ z-index: 50;
+ cursor: grab;
+
+ &:hover {
+ background-color: var(--novel-stone-100);
+ transition: background-color 0.2s;
+ }
+
+ &:active {
+ background-color: var(--novel-stone-200);
+ transition: background-color 0.2s;
+ cursor: grabbing;
+ }
+
+ &.hide {
+ opacity: 0;
+ pointer-events: none;
+ }
+
+ @media screen and (max-width: 600px) {
+ display: none;
+ pointer-events: none;
+ }
+}
+
+.dark .drag-handle {
+ background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 10 10' style='fill: rgba(255, 255, 255, 0.5)'%3E%3Cpath d='M3,2 C2.44771525,2 2,1.55228475 2,1 C2,0.44771525 2.44771525,0 3,0 C3.55228475,0 4,0.44771525 4,1 C4,1.55228475 3.55228475,2 3,2 Z M3,6 C2.44771525,6 2,5.55228475 2,5 C2,4.44771525 2.44771525,4 3,4 C3.55228475,4 4,4.44771525 4,5 C4,5.55228475 3.55228475,6 3,6 Z M3,10 C2.44771525,10 2,9.55228475 2,9 C2,8.44771525 2.44771525,8 3,8 C3.55228475,8 4,8.44771525 4,9 C4,9.55228475 3.55228475,10 3,10 Z M7,2 C6.44771525,2 6,1.55228475 6,1 C6,0.44771525 6.44771525,0 7,0 C7.55228475,0 8,0.44771525 8,1 C8,1.55228475 7.55228475,2 7,2 Z M7,6 C6.44771525,6 6,5.55228475 6,5 C6,4.44771525 6.44771525,4 7,4 C7.55228475,4 8,4.44771525 8,5 C8,5.55228475 7.55228475,6 7,6 Z M7,10 C6.44771525,10 6,9.55228475 6,9 C6,8.44771525 6.44771525,8 7,8 C7.55228475,8 8,8.44771525 8,9 C8,9.55228475 7.55228475,10 7,10 Z'%3E%3C/path%3E%3C/svg%3E");
+}
+
+/* Custom Youtube Video CSS */
+iframe {
+ border: 8px solid #ffd00027;
+ border-radius: 4px;
+ min-width: 200px;
+ min-height: 200px;
+ display: block;
+ outline: 0px solid transparent;
+}
+
+div[data-youtube-video] > iframe {
+ cursor: move;
+ aspect-ratio: 16 / 9;
+ width: 100%;
+}
+
+.ProseMirror-selectednode iframe {
+ transition: outline 0.15s;
+ outline: 6px solid #fbbf24;
+}
+
+@media only screen and (max-width: 480px) {
+ div[data-youtube-video] > iframe {
+ max-height: 50px;
+ }
+}
+
+@media only screen and (max-width: 720px) {
+ div[data-youtube-video] > iframe {
+ max-height: 100px;
+ }
+} \ No newline at end of file
diff --git a/apps/web/app/(landing)/package.json b/apps/web/app/(landing)/package.json
deleted file mode 100644
index a7fabf2f..00000000
--- a/apps/web/app/(landing)/package.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "scripts": {
- "vercel-build": "next build"
- }
-}
diff --git a/apps/web/app/(landing)/page.tsx b/apps/web/app/(landing)/page.tsx
index 562e9af9..5f8b28b4 100644
--- a/apps/web/app/(landing)/page.tsx
+++ b/apps/web/app/(landing)/page.tsx
@@ -5,7 +5,7 @@ import Cta from "./Cta";
import { Toaster } from "@repo/ui/shadcn/toaster";
import Features from "./Features";
import Footer from "./footer";
-import { auth } from "../helpers/server/auth";
+import { auth } from "../../server/auth";
import { redirect } from "next/navigation";
export const runtime = "edge";
@@ -16,7 +16,7 @@ export default async function Home() {
console.log(user);
if (user) {
- // await redirect("/home")
+ await redirect("/home");
}
return (
diff --git a/apps/web/app/actions/doers.ts b/apps/web/app/actions/doers.ts
new file mode 100644
index 00000000..6c7180d9
--- /dev/null
+++ b/apps/web/app/actions/doers.ts
@@ -0,0 +1,268 @@
+"use server";
+
+import { revalidatePath } from "next/cache";
+import { db } from "../../server/db";
+import { contentToSpace, space, storedContent } from "../../server/db/schema";
+import { ServerActionReturnType } from "./types";
+import { auth } from "../../server/auth";
+import { Tweet } from "react-tweet/api";
+import { getMetaData } from "@/lib/get-metadata";
+import { and, eq, inArray, sql } from "drizzle-orm";
+import { LIMITS } from "@/lib/constants";
+import { z } from "zod";
+
+export const createSpace = async (
+ input: string | FormData,
+): ServerActionReturnType<number> => {
+ const data = await auth();
+
+ if (!data || !data.user) {
+ return { error: "Not authenticated", success: false };
+ }
+
+ if (typeof input === "object") {
+ input = (input as FormData).get("name") as string;
+ }
+
+ try {
+ const resp = await db
+ .insert(space)
+ .values({ name: input, user: data.user.id });
+
+ revalidatePath("/home");
+ return { success: true, data: 1 };
+ } catch (e: unknown) {
+ const error = e as Error;
+ if (
+ error.message.includes("D1_ERROR: UNIQUE constraint failed: space.name")
+ ) {
+ return { success: false, data: 0, error: "Space already exists" };
+ } else {
+ return {
+ success: false,
+ data: 0,
+ error: "Failed to create space with error: " + error.message,
+ };
+ }
+ }
+};
+
+const typeDecider = (content: string) => {
+ // if the content is a URL, then it's a page. if its a URL with https://x.com/user/status/123, then it's a tweet. else, it's a note.
+ // do strict checking with regex
+ if (content.match(/https?:\/\/[\w\.]+\/[\w]+\/[\w]+\/[\d]+/)) {
+ return "tweet";
+ } else if (content.match(/https?:\/\/[\w\.]+/)) {
+ return "page";
+ } else {
+ return "note";
+ }
+};
+
+export const limit = async (userId: string, type = "page") => {
+ const count = await db
+ .select({
+ count: sql<number>`count(*)`.mapWith(Number),
+ })
+ .from(storedContent)
+ .where(and(eq(storedContent.userId, userId), eq(storedContent.type, type)));
+
+ if (count[0]!.count > LIMITS[type as keyof typeof LIMITS]) {
+ return false;
+ }
+
+ return true;
+};
+
+const getTweetData = async (tweetID: string) => {
+ const url = `https://cdn.syndication.twimg.com/tweet-result?id=${tweetID}&lang=en&features=tfw_timeline_list%3A%3Btfw_follower_count_sunset%3Atrue%3Btfw_tweet_edit_backend%3Aon%3Btfw_refsrc_session%3Aon%3Btfw_fosnr_soft_interventions_enabled%3Aon%3Btfw_show_birdwatch_pivots_enabled%3Aon%3Btfw_show_business_verified_badge%3Aon%3Btfw_duplicate_scribes_to_settings%3Aon%3Btfw_use_profile_image_shape_enabled%3Aon%3Btfw_show_blue_verified_badge%3Aon%3Btfw_legacy_timeline_sunset%3Atrue%3Btfw_show_gov_verified_badge%3Aon%3Btfw_show_business_affiliate_badge%3Aon%3Btfw_tweet_edit_frontend%3Aon&token=4c2mmul6mnh`;
+
+ const resp = await fetch(url, {
+ headers: {
+ "User-Agent":
+ "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3",
+ Accept: "application/json",
+ "Accept-Language": "en-US,en;q=0.5",
+ "Accept-Encoding": "gzip, deflate, br",
+ Connection: "keep-alive",
+ "Upgrade-Insecure-Requests": "1",
+ "Cache-Control": "max-age=0",
+ TE: "Trailers",
+ },
+ });
+ console.log(resp.status);
+ const data = (await resp.json()) as Tweet;
+
+ return data;
+};
+
+export const createMemory = async (input: {
+ content: string;
+ spaces?: string[];
+}): ServerActionReturnType<number> => {
+ const data = await auth();
+
+ if (!data || !data.user || !data.user.id) {
+ return { error: "Not authenticated", success: false };
+ }
+
+ const type = typeDecider(input.content);
+
+ let pageContent = input.content;
+ let metadata: Awaited<ReturnType<typeof getMetaData>>;
+
+ if (!(await limit(data.user.id, type))) {
+ return {
+ success: false,
+ data: 0,
+ error: `You have exceeded the limit of ${LIMITS[type as keyof typeof LIMITS]} ${type}s.`,
+ };
+ }
+
+ if (type === "page") {
+ const response = await fetch("https://md.dhr.wtf/?url=" + input.content, {
+ headers: {
+ Authorization: "Bearer " + process.env.BACKEND_SECURITY_KEY,
+ },
+ });
+ pageContent = await response.text();
+ metadata = await getMetaData(input.content);
+ } else if (type === "tweet") {
+ const tweet = await getTweetData(input.content.split("/").pop() as string);
+ pageContent = JSON.stringify(tweet);
+ metadata = {
+ baseUrl: input.content,
+ description: tweet.text,
+ image: tweet.user.profile_image_url_https,
+ title: `Tweet by ${tweet.user.name}`,
+ };
+ } else if (type === "note") {
+ pageContent = input.content;
+ const noteId = new Date().getTime();
+ metadata = {
+ baseUrl: `https://supermemory.ai/note/${noteId}`,
+ description: `Note created at ${new Date().toLocaleString()}`,
+ image: "https://supermemory.ai/logo.png",
+ title: `${pageContent.slice(0, 20)} ${pageContent.length > 20 ? "..." : ""}`,
+ };
+ } else {
+ return {
+ success: false,
+ data: 0,
+ error: "Invalid type",
+ };
+ }
+
+ let storeToSpaces = input.spaces;
+
+ if (!storeToSpaces) {
+ storeToSpaces = [];
+ }
+
+ const vectorSaveResponse = await fetch(
+ `${process.env.BACKEND_BASE_URL}/api/add`,
+ {
+ method: "POST",
+ body: JSON.stringify({
+ pageContent,
+ title: metadata.title,
+ description: metadata.description,
+ url: metadata.baseUrl,
+ spaces: storeToSpaces,
+ user: data.user.id,
+ type,
+ }),
+ headers: {
+ "Content-Type": "application/json",
+ Authorization: "Bearer " + process.env.BACKEND_SECURITY_KEY,
+ },
+ },
+ );
+
+ if (!vectorSaveResponse.ok) {
+ const errorData = await vectorSaveResponse.text();
+ console.log(errorData);
+ return {
+ success: false,
+ data: 0,
+ error: `Failed to save to vector store. Backend returned error: ${errorData}`,
+ };
+ }
+
+ // Insert into database
+ const insertResponse = await db
+ .insert(storedContent)
+ .values({
+ content: pageContent,
+ title: metadata.title,
+ description: metadata.description,
+ url: input.content,
+ baseUrl: metadata.baseUrl,
+ image: metadata.image,
+ savedAt: new Date(),
+ userId: data.user.id,
+ type,
+ })
+ .returning({ id: storedContent.id });
+
+ const contentId = insertResponse[0]?.id;
+ if (!contentId) {
+ return {
+ success: false,
+ data: 0,
+ error: "Something went wrong while saving the document to the database",
+ };
+ }
+
+ if (storeToSpaces.length > 0) {
+ // Adding the many-to-many relationship between content and spaces
+ const spaceData = await db
+ .select()
+ .from(space)
+ .where(
+ and(
+ inArray(
+ space.id,
+ storeToSpaces.map((s) => parseInt(s)),
+ ),
+ eq(space.user, data.user.id),
+ ),
+ )
+ .all();
+
+ await Promise.all(
+ spaceData.map(async (space) => {
+ await db
+ .insert(contentToSpace)
+ .values({ contentId: contentId, spaceId: space.id });
+ }),
+ );
+ }
+
+ try {
+ const response = await vectorSaveResponse.json();
+
+ const expectedResponse = z.object({ status: z.literal("ok") });
+
+ const parsedResponse = expectedResponse.safeParse(response);
+
+ if (!parsedResponse.success) {
+ return {
+ success: false,
+ data: 0,
+ error: `Failed to save to vector store. Backend returned error: ${parsedResponse.error.message}`,
+ };
+ }
+
+ return {
+ success: true,
+ data: 1,
+ };
+ } catch (e) {
+ return {
+ success: false,
+ data: 0,
+ error: `Failed to save to vector store. Backend returned error: ${e}`,
+ };
+ }
+};
diff --git a/apps/web/app/actions/fetchers.ts b/apps/web/app/actions/fetchers.ts
new file mode 100644
index 00000000..dc71252e
--- /dev/null
+++ b/apps/web/app/actions/fetchers.ts
@@ -0,0 +1,142 @@
+"use server";
+
+import { eq, inArray, not, sql } from "drizzle-orm";
+import { db } from "../../server/db";
+import {
+ Content,
+ contentToSpace,
+ storedContent,
+ users,
+} from "../../server/db/schema";
+import { ServerActionReturnType, Space } from "./types";
+import { auth } from "../../server/auth";
+
+export const getSpaces = async (): ServerActionReturnType<Space[]> => {
+ const data = await auth();
+
+ if (!data || !data.user) {
+ return { error: "Not authenticated", success: false };
+ }
+
+ const spaces = await db.query.space.findMany({
+ where: eq(users, data.user.id),
+ });
+
+ const spacesWithoutUser = spaces.map((space) => {
+ return { ...space, user: undefined };
+ });
+
+ return { success: true, data: spacesWithoutUser };
+};
+
+export const getAllMemories = async (
+ freeMemoriesOnly: boolean = false,
+): ServerActionReturnType<Content[]> => {
+ const data = await auth();
+
+ if (!data || !data.user) {
+ return { error: "Not authenticated", success: false };
+ }
+
+ if (!freeMemoriesOnly) {
+ // Returns all memories, no matter the space.
+ const memories = await db.query.storedContent.findMany({
+ where: eq(users, data.user.id),
+ });
+
+ return { success: true, data: memories };
+ }
+
+ // This only returns memories that are not a part of any space.
+ // This is useful for home page where we want to show a list of spaces and memories.
+ const contentNotInAnySpace = await db
+ .select()
+ .from(storedContent)
+ .where(
+ not(
+ eq(
+ storedContent.id,
+ db
+ .select({ contentId: contentToSpace.contentId })
+ .from(contentToSpace),
+ ),
+ ),
+ )
+ .execute();
+
+ return { success: true, data: contentNotInAnySpace };
+};
+
+export const getAllUserMemoriesAndSpaces = async (): ServerActionReturnType<{
+ spaces: Space[];
+ memories: Content[];
+}> => {
+ const data = await auth();
+
+ if (!data || !data.user) {
+ return { error: "Not authenticated", success: false };
+ }
+
+ const spaces = await db.query.space.findMany({
+ where: eq(users, data.user.id),
+ });
+
+ const spacesWithoutUser = spaces.map((space) => {
+ return { ...space, user: undefined };
+ });
+
+ // const contentCountBySpace = await db
+ // .select({
+ // spaceId: contentToSpace.spaceId,
+ // count: sql<number>`count(*)`.mapWith(Number),
+ // })
+ // .from(contentToSpace)
+ // .where(
+ // inArray(
+ // contentToSpace.spaceId,
+ // spacesWithoutUser.map((space) => space.id),
+ // ),
+ // )
+ // .groupBy(contentToSpace.spaceId)
+ // .execute();
+
+ // console.log(contentCountBySpace);
+
+ // get a count with space mappings like spaceID: count (number of memories in that space)
+ const contentCountBySpace = await db
+ .select({
+ spaceId: contentToSpace.spaceId,
+ count: sql<number>`count(*)`.mapWith(Number),
+ })
+ .from(contentToSpace)
+ .where(
+ inArray(
+ contentToSpace.spaceId,
+ spacesWithoutUser.map((space) => space.id),
+ ),
+ )
+ .groupBy(contentToSpace.spaceId)
+ .execute();
+
+ console.log(contentCountBySpace);
+
+ const contentNotInAnySpace = await db
+ .select()
+ .from(storedContent)
+ .where(
+ not(
+ eq(
+ storedContent.id,
+ db
+ .select({ contentId: contentToSpace.contentId })
+ .from(contentToSpace),
+ ),
+ ),
+ )
+ .execute();
+
+ return {
+ success: true,
+ data: { spaces: spacesWithoutUser, memories: contentNotInAnySpace },
+ };
+};
diff --git a/apps/web/app/actions/types.ts b/apps/web/app/actions/types.ts
new file mode 100644
index 00000000..5c5afc5c
--- /dev/null
+++ b/apps/web/app/actions/types.ts
@@ -0,0 +1,11 @@
+export type Space = {
+ id: number;
+ name: string;
+ numberOfMemories?: number;
+};
+
+export type ServerActionReturnType<T> = Promise<{
+ error?: string;
+ success: boolean;
+ data?: T;
+}>;
diff --git a/apps/web/app/api/[...nextauth]/route.ts b/apps/web/app/api/[...nextauth]/route.ts
index 50807ab1..e19cc16e 100644
--- a/apps/web/app/api/[...nextauth]/route.ts
+++ b/apps/web/app/api/[...nextauth]/route.ts
@@ -1,2 +1,2 @@
-export { GET, POST } from "../../helpers/server/auth";
+export { GET, POST } from "../../../server/auth";
export const runtime = "edge";
diff --git a/apps/web/app/api/chat/route.ts b/apps/web/app/api/chat/route.ts
index 34099848..c19ce92b 100644
--- a/apps/web/app/api/chat/route.ts
+++ b/apps/web/app/api/chat/route.ts
@@ -1,6 +1,11 @@
import { type NextRequest } from "next/server";
-import { ChatHistory } from "@repo/shared-types";
+import {
+ ChatHistory,
+ ChatHistoryZod,
+ convertChatHistoryList,
+} from "@repo/shared-types";
import { ensureAuth } from "../ensureAuth";
+import { z } from "zod";
export const runtime = "edge";
@@ -15,59 +20,69 @@ export async function POST(req: NextRequest) {
return new Response("Missing BACKEND_SECURITY_KEY", { status: 500 });
}
- const query = new URL(req.url).searchParams.get("q");
- const spaces = new URL(req.url).searchParams.get("spaces");
+ const url = new URL(req.url);
- const sourcesOnly =
- new URL(req.url).searchParams.get("sourcesOnly") ?? "false";
+ const query = url.searchParams.get("q");
+ const spaces = url.searchParams.get("spaces");
- const chatHistory = (await req.json()) as {
- chatHistory: ChatHistory[];
- };
+ const sourcesOnly = url.searchParams.get("sourcesOnly") ?? "false";
- console.log("CHathistory", chatHistory);
+ const chatHistory = await req.json();
- if (!query) {
+ if (!query || query.trim.length < 0) {
return new Response(JSON.stringify({ message: "Invalid query" }), {
status: 400,
});
}
- try {
- const resp = await fetch(
- `https://cf-ai-backend.dhravya.workers.dev/chat?q=${query}&user=${session.user.email ?? session.user.name}&sourcesOnly=${sourcesOnly}&spaces=${spaces}`,
- {
- headers: {
- "X-Custom-Auth-Key": process.env.BACKEND_SECURITY_KEY!,
- },
- method: "POST",
- body: JSON.stringify({
- chatHistory: chatHistory.chatHistory ?? [],
- }),
+ const validated = z
+ .object({ chatHistory: z.array(ChatHistoryZod) })
+ .safeParse(chatHistory ?? []);
+
+ if (!validated.success) {
+ return new Response(
+ JSON.stringify({
+ message: "Invalid chat history",
+ error: validated.error,
+ }),
+ { status: 400 },
+ );
+ }
+
+ const modelCompatible = await convertChatHistoryList(
+ validated.data.chatHistory,
+ );
+
+ const resp = await fetch(
+ `${process.env.BACKEND_BASE_URL}/api/chat?query=${query}&user=${session.user.id}&sourcesOnly=${sourcesOnly}&spaces=${spaces}`,
+ {
+ headers: {
+ Authorization: `Bearer ${process.env.BACKEND_SECURITY_KEY}`,
+ "Content-Type": "application/json",
},
+ method: "POST",
+ body: JSON.stringify({
+ chatHistory: modelCompatible,
+ }),
+ },
+ );
+
+ console.log("sourcesOnly", sourcesOnly);
+
+ if (sourcesOnly == "true") {
+ const data = await resp.json();
+ console.log("data", data);
+ return new Response(JSON.stringify(data), { status: 200 });
+ }
+
+ if (resp.status !== 200 || !resp.ok) {
+ const errorData = await resp.text();
+ console.log(errorData);
+ return new Response(
+ JSON.stringify({ message: "Error in CF function", error: errorData }),
+ { status: resp.status },
);
+ }
- console.log("sourcesOnly", sourcesOnly);
-
- if (sourcesOnly == "true") {
- const data = await resp.json();
- console.log("data", data);
- return new Response(JSON.stringify(data), { status: 200 });
- }
-
- if (resp.status !== 200 || !resp.ok) {
- const errorData = await resp.json();
- console.log(errorData);
- return new Response(
- JSON.stringify({ message: "Error in CF function", error: errorData }),
- { status: resp.status },
- );
- }
-
- // Stream the response back to the client
- const { readable, writable } = new TransformStream();
- resp && resp.body!.pipeTo(writable);
-
- return new Response(readable, { status: 200 });
- } catch {}
+ return new Response(resp.body, { status: 200 });
}
diff --git a/apps/web/app/api/editorai/route.ts b/apps/web/app/api/editorai/route.ts
new file mode 100644
index 00000000..6ee0aed2
--- /dev/null
+++ b/apps/web/app/api/editorai/route.ts
@@ -0,0 +1,20 @@
+import type { NextRequest } from "next/server";
+import { ensureAuth } from "../ensureAuth";
+
+export const runtime = "edge";
+
+export async function POST(request: NextRequest) {
+ const d = await ensureAuth(request);
+ if (!d) {
+ return new Response("Unauthorized", { status: 401 });
+ }
+ const res : {context: string, request: string} = await request.json()
+
+ try {
+ const response = await fetch(`${process.env.BACKEND_BASE_URL}/api/editorai?context=${res.context}&request=${res.request}`);
+ const result = await response.json();
+ return new Response(JSON.stringify(result));
+ } catch (error) {
+ return new Response(`Error, ${error}`)
+ }
+} \ No newline at end of file
diff --git a/apps/web/app/api/ensureAuth.ts b/apps/web/app/api/ensureAuth.ts
index a1401a07..d2fbac0b 100644
--- a/apps/web/app/api/ensureAuth.ts
+++ b/apps/web/app/api/ensureAuth.ts
@@ -1,6 +1,6 @@
import { NextRequest } from "next/server";
-import { db } from "../helpers/server/db";
-import { sessions, users } from "../helpers/server/db/schema";
+import { db } from "../../server/db";
+import { sessions, users } from "../../server/db/schema";
import { eq } from "drizzle-orm";
export async function ensureAuth(req: NextRequest) {
diff --git a/apps/web/app/api/getCount/route.ts b/apps/web/app/api/getCount/route.ts
index f760c145..7cd2a2d3 100644
--- a/apps/web/app/api/getCount/route.ts
+++ b/apps/web/app/api/getCount/route.ts
@@ -1,6 +1,6 @@
-import { db } from "@/app/helpers/server/db";
+import { db } from "@/server/db";
import { and, eq, ne, sql } from "drizzle-orm";
-import { sessions, storedContent, users } from "@/app/helpers/server/db/schema";
+import { sessions, storedContent, users } from "@/server/db/schema";
import { type NextRequest, NextResponse } from "next/server";
import { ensureAuth } from "../ensureAuth";
@@ -20,7 +20,7 @@ export async function GET(req: NextRequest) {
.from(storedContent)
.where(
and(
- eq(storedContent.user, session.user.id),
+ eq(storedContent.userId, session.user.id),
eq(storedContent.type, "twitter-bookmark"),
),
);
@@ -32,7 +32,7 @@ export async function GET(req: NextRequest) {
.from(storedContent)
.where(
and(
- eq(storedContent.user, session.user.id),
+ eq(storedContent.userId, session.user.id),
ne(storedContent.type, "twitter-bookmark"),
),
);
diff --git a/apps/web/app/api/me/route.ts b/apps/web/app/api/me/route.ts
index 20b6aece..621dcbfe 100644
--- a/apps/web/app/api/me/route.ts
+++ b/apps/web/app/api/me/route.ts
@@ -1,6 +1,6 @@
-import { db } from "@/app/helpers/server/db";
+import { db } from "@/server/db";
import { eq } from "drizzle-orm";
-import { sessions, users } from "@/app/helpers/server/db/schema";
+import { sessions, users } from "@/server/db/schema";
import { type NextRequest, NextResponse } from "next/server";
export const runtime = "edge";
diff --git a/apps/web/app/api/spaces/route.ts b/apps/web/app/api/spaces/route.ts
index c46b02fc..cbed547d 100644
--- a/apps/web/app/api/spaces/route.ts
+++ b/apps/web/app/api/spaces/route.ts
@@ -1,5 +1,5 @@
-import { db } from "@/app/helpers/server/db";
-import { sessions, space, users } from "@/app/helpers/server/db/schema";
+import { db } from "@/server/db";
+import { sessions, space, users } from "@/server/db/schema";
import { eq } from "drizzle-orm";
import { NextRequest, NextResponse } from "next/server";
import { ensureAuth } from "../ensureAuth";
diff --git a/apps/web/app/api/store/route.ts b/apps/web/app/api/store/route.ts
index f96f90cf..cb10db24 100644
--- a/apps/web/app/api/store/route.ts
+++ b/apps/web/app/api/store/route.ts
@@ -1,4 +1,4 @@
-import { db } from "@/app/helpers/server/db";
+import { db } from "@/server/db";
import { and, eq, sql, inArray } from "drizzle-orm";
import {
contentToSpace,
@@ -6,10 +6,12 @@ import {
storedContent,
users,
space,
-} from "@/app/helpers/server/db/schema";
+} from "@/server/db/schema";
import { type NextRequest, NextResponse } from "next/server";
-import { getMetaData } from "@/app/helpers/lib/get-metadata";
+import { getMetaData } from "@/lib/get-metadata";
import { ensureAuth } from "../ensureAuth";
+import { limit } from "@/app/actions/doers";
+import { LIMITS } from "@/lib/constants";
export const runtime = "edge";
@@ -33,22 +35,13 @@ export async function POST(req: NextRequest) {
storeToSpaces = [];
}
- const count = await db
- .select({
- count: sql<number>`count(*)`.mapWith(Number),
- })
- .from(storedContent)
- .where(
- and(
- eq(storedContent.user, session.user.id),
- eq(storedContent.type, "page"),
- ),
- );
-
- if (count[0]!.count > 100) {
+ if (!(await limit(session.user.id))) {
return NextResponse.json(
- { message: "Error", error: "Limit exceeded" },
- { status: 499 },
+ {
+ message: "Error: Ratelimit exceeded",
+ error: `You have exceeded the limit of ${LIMITS["page"]} pages.`,
+ },
+ { status: 429 },
);
}
@@ -62,7 +55,7 @@ export async function POST(req: NextRequest) {
baseUrl: metadata.baseUrl,
image: metadata.image,
savedAt: new Date(),
- user: session.user.id,
+ userId: session.user.id,
})
.returning({ id: storedContent.id });
diff --git a/apps/web/app/api/unfirlsite/route.ts b/apps/web/app/api/unfirlsite/route.ts
new file mode 100644
index 00000000..4b8b4858
--- /dev/null
+++ b/apps/web/app/api/unfirlsite/route.ts
@@ -0,0 +1,134 @@
+import { load } from 'cheerio'
+import { AwsClient } from "aws4fetch";
+
+import type { NextRequest } from "next/server";
+import { ensureAuth } from "../ensureAuth";
+
+export const runtime = "edge";
+
+const r2 = new AwsClient({
+ accessKeyId: process.env.R2_ACCESS_KEY_ID,
+ secretAccessKey: process.env.R2_SECRET_ACCESS_KEY,
+});
+
+
+export async function POST(request: NextRequest) {
+
+ const d = await ensureAuth(request);
+ if (!d) {
+ return new Response("Unauthorized", { status: 401 });
+ }
+
+ if (
+ !process.env.R2_ACCESS_KEY_ID ||
+ !process.env.R2_ACCOUNT_ID ||
+ !process.env.R2_SECRET_ACCESS_KEY ||
+ !process.env.R2_BUCKET_NAME
+ ) {
+ return new Response(
+ "Missing one or more R2 env variables: R2_ENDPOINT, R2_ACCESS_ID, R2_SECRET_KEY, R2_BUCKET_NAME. To get them, go to the R2 console, create and paste keys in a `.dev.vars` file in the root of this project.",
+ { status: 500 },
+ );
+ }
+
+ const website = new URL(request.url).searchParams.get("website");
+
+ if (!website) {
+ return new Response("Missing website", { status: 400 });
+ }
+
+ const salt = () => Math.floor(Math.random() * 11);
+ const encodeWebsite = `${encodeURIComponent(website)}${salt()}`;
+
+ try {
+ // this returns the og image, description and title of website
+ const response = await unfurl(website);
+
+ if (!response.image){
+ return new Response(JSON.stringify(response))
+ }
+
+ const imageUrl = await process.env.DEV_IMAGES.get(encodeWebsite)
+ if (imageUrl){
+ return new Response(JSON.stringify({
+ image: imageUrl,
+ title: response.title,
+ description: response.description,
+ }))
+ }
+
+ const res = await fetch(`${response.image}`)
+ const image = await res.blob();
+
+ const url = new URL(
+ `https://${process.env.R2_BUCKET_NAME}.${process.env.R2_ACCOUNT_ID}.r2.cloudflarestorage.com`
+ );
+
+ url.pathname = encodeWebsite;
+ url.searchParams.set("X-Amz-Expires", "3600");
+
+ const signedPuturl = await r2.sign(
+ new Request(url, {
+ method: "PUT",
+ }),
+ {
+ aws: { signQuery: true },
+ }
+ );
+ await fetch(signedPuturl.url, {
+ method: 'PUT',
+ body: image,
+ });
+
+ await process.env.DEV_IMAGES.put(encodeWebsite, `${process.env.R2_PUBLIC_BUCKET_ADDRESS}/${encodeWebsite}`)
+
+ return new Response(JSON.stringify({
+ image: `${process.env.R2_PUBLIC_BUCKET_ADDRESS}/${encodeWebsite}`,
+ title: response.title,
+ description: response.description,
+ }));
+
+ } catch (error) {
+ console.log(error)
+ return new Response(JSON.stringify({
+ status: 500,
+ error: error,
+ }))
+ }
+ }
+
+export async function unfurl(url: string) {
+ const response = await fetch(url)
+ if (response.status >= 400) {
+ throw new Error(`Error fetching url: ${response.status}`)
+ }
+ const contentType = response.headers.get('content-type')
+ if (!contentType?.includes('text/html')) {
+ throw new Error(`Content-type not right: ${contentType}`)
+ }
+
+ const content = await response.text()
+ const $ = load(content)
+
+ const og: { [key: string]: string | undefined } = {}
+ const twitter: { [key: string]: string | undefined } = {}
+
+ // @ts-ignore, it just works so why care of type safety if someone has better way go ahead
+ $('meta[property^=og:]').each((_, el) => (og[$(el).attr('property')!] = $(el).attr('content')))
+ // @ts-ignore
+ $('meta[name^=twitter:]').each((_, el) => (twitter[$(el).attr('name')!] = $(el).attr('content')))
+
+ const title = og['og:title'] ?? twitter['twitter:title'] ?? $('title').text() ?? undefined
+ const description =
+ og['og:description'] ??
+ twitter['twitter:description'] ??
+ $('meta[name="description"]').attr('content') ??
+ undefined
+ const image = og['og:image:secure_url'] ?? og['og:image'] ?? twitter['twitter:image'] ?? undefined
+
+ return {
+ title,
+ description,
+ image,
+ }
+}
diff --git a/apps/web/app/layout.tsx b/apps/web/app/layout.tsx
index 1b5558bb..52cd9d32 100644
--- a/apps/web/app/layout.tsx
+++ b/apps/web/app/layout.tsx
@@ -2,6 +2,7 @@ import "@repo/tailwind-config/globals.css";
import type { Metadata } from "next";
import { Inter } from "next/font/google";
+import { Toaster } from "@repo/ui/shadcn/toaster";
const inter = Inter({ subsets: ["latin"] });
@@ -63,7 +64,10 @@ export default function RootLayout({
return (
<html lang="en">
{/* TODO: when lightmode support is added, remove the 'dark' class from the body tag */}
- <body className={`${inter.className}`}>{children}</body>
+ <body className={`${inter.className} dark`}>
+ {children}
+ <Toaster />
+ </body>
</html>
);
}
diff --git a/apps/web/app/ref/page.tsx b/apps/web/app/ref/page.tsx
index 9ace733a..b51a16bb 100644
--- a/apps/web/app/ref/page.tsx
+++ b/apps/web/app/ref/page.tsx
@@ -1,9 +1,9 @@
import { Button } from "@repo/ui/shadcn/button";
-import { auth, signIn, signOut } from "../helpers/server/auth";
-import { db } from "../helpers/server/db";
+import { auth, signIn, signOut } from "../../server/auth";
+import { db } from "../../server/db";
import { sql } from "drizzle-orm";
-import { users } from "../helpers/server/db/schema";
-import { getThemeToggler } from "../helpers/lib/get-theme-button";
+import { users } from "../../server/db/schema";
+import { getThemeToggler } from "../../lib/get-theme-button";
export const runtime = "edge";
diff --git a/apps/web/cf-env.d.ts b/apps/web/cf-env.d.ts
index 98303f35..be5c991a 100644
--- a/apps/web/cf-env.d.ts
+++ b/apps/web/cf-env.d.ts
@@ -1,6 +1,17 @@
declare global {
namespace NodeJS {
- interface ProcessEnv extends CloudflareEnv {}
+ interface ProcessEnv extends CloudflareEnv {
+ GOOGLE_CLIENT_ID: string;
+ GOOGLE_CLIENT_SECRET: string;
+ AUTH_SECRET: string;
+ R2_ENDPOINT: string;
+ R2_ACCESS_KEY_ID: string;
+ R2_SECRET_ACCESS_KEY: string;
+ R2_PUBLIC_BUCKET_ADDRESS: string;
+ R2_BUCKET_NAME: string;
+ BACKEND_SECURITY_KEY: string;
+ BACKEND_BASE_URL: string;
+ }
}
}
diff --git a/apps/web/env.d.ts b/apps/web/env.d.ts
index 2755280c..4f11ba55 100644
--- a/apps/web/env.d.ts
+++ b/apps/web/env.d.ts
@@ -2,14 +2,6 @@
// by running `wrangler types --env-interface CloudflareEnv env.d.ts`
interface CloudflareEnv {
- GOOGLE_CLIENT_ID: string;
- GOOGLE_CLIENT_SECRET: string;
- AUTH_SECRET: string;
- R2_ENDPOINT: string;
- R2_ACCESS_ID: string;
- R2_SECRET_KEY: string;
- R2_BUCKET_NAME: string;
- BACKEND_SECURITY_KEY: string;
STORAGE: R2Bucket;
DATABASE: D1Database;
}
diff --git a/apps/web/lib/constants.ts b/apps/web/lib/constants.ts
new file mode 100644
index 00000000..7a9485cf
--- /dev/null
+++ b/apps/web/lib/constants.ts
@@ -0,0 +1,43 @@
+export const LIMITS = {
+ page: 100,
+ tweet: 1000,
+ note: 1000,
+};
+
+export const codeLanguageSubset = [
+ "python",
+ "javascript",
+ "java",
+ "go",
+ "bash",
+ "c",
+ "cpp",
+ "csharp",
+ "css",
+ "diff",
+ "graphql",
+ "json",
+ "kotlin",
+ "less",
+ "lua",
+ "makefile",
+ "markdown",
+ "objectivec",
+ "perl",
+ "php",
+ "php-template",
+ "plaintext",
+ "python-repl",
+ "r",
+ "ruby",
+ "rust",
+ "scss",
+ "shell",
+ "sql",
+ "swift",
+ "typescript",
+ "vbnet",
+ "wasm",
+ "xml",
+ "yaml",
+];
diff --git a/apps/web/app/helpers/lib/get-metadata.ts b/apps/web/lib/get-metadata.ts
index 4609e49b..4609e49b 100644
--- a/apps/web/app/helpers/lib/get-metadata.ts
+++ b/apps/web/lib/get-metadata.ts
diff --git a/apps/web/app/helpers/lib/get-theme-button.tsx b/apps/web/lib/get-theme-button.tsx
index 020cc976..020cc976 100644
--- a/apps/web/app/helpers/lib/get-theme-button.tsx
+++ b/apps/web/lib/get-theme-button.tsx
diff --git a/apps/web/app/helpers/lib/handle-errors.ts b/apps/web/lib/handle-errors.ts
index 42cae589..42cae589 100644
--- a/apps/web/app/helpers/lib/handle-errors.ts
+++ b/apps/web/lib/handle-errors.ts
diff --git a/apps/web/app/helpers/lib/searchParams.ts b/apps/web/lib/searchParams.ts
index 9899eaf7..9899eaf7 100644
--- a/apps/web/app/helpers/lib/searchParams.ts
+++ b/apps/web/lib/searchParams.ts
diff --git a/apps/web/migrations/000_setup.sql b/apps/web/migrations/000_setup.sql
index db7f9444..0c151b98 100644
--- a/apps/web/migrations/000_setup.sql
+++ b/apps/web/migrations/000_setup.sql
@@ -1,18 +1,29 @@
CREATE TABLE `account` (
- `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
- `userId` text(255) NOT NULL,
- `type` text(255) NOT NULL,
- `provider` text(255) NOT NULL,
- `providerAccountId` text(255) NOT NULL,
+ `userId` text NOT NULL,
+ `type` text NOT NULL,
+ `provider` text NOT NULL,
+ `providerAccountId` text NOT NULL,
`refresh_token` text,
`access_token` text,
`expires_at` integer,
- `token_type` text(255),
- `scope` text(255),
+ `token_type` text,
+ `scope` text,
`id_token` text,
- `session_state` text(255),
- `oauth_token_secret` text,
- `oauth_token` text,
+ `session_state` text,
+ PRIMARY KEY(`provider`, `providerAccountId`),
+ FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
+);
+--> statement-breakpoint
+CREATE TABLE `authenticator` (
+ `credentialID` text NOT NULL,
+ `userId` text NOT NULL,
+ `providerAccountId` text NOT NULL,
+ `credentialPublicKey` text NOT NULL,
+ `counter` integer NOT NULL,
+ `credentialDeviceType` text NOT NULL,
+ `credentialBackedUp` integer NOT NULL,
+ `transports` text,
+ PRIMARY KEY(`credentialID`, `userId`),
FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
@@ -25,9 +36,8 @@ CREATE TABLE `contentToSpace` (
);
--> statement-breakpoint
CREATE TABLE `session` (
- `id` integer PRIMARY KEY AUTOINCREMENT NOT NULL,
- `sessionToken` text(255) NOT NULL,
- `userId` text(255) NOT NULL,
+ `sessionToken` text PRIMARY KEY NOT NULL,
+ `userId` text NOT NULL,
`expires` integer NOT NULL,
FOREIGN KEY (`userId`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
@@ -50,27 +60,26 @@ CREATE TABLE `storedContent` (
`ogImage` text(255),
`type` text DEFAULT 'page',
`image` text(255),
- `user` text(255),
+ `user` integer,
FOREIGN KEY (`user`) REFERENCES `user`(`id`) ON UPDATE no action ON DELETE cascade
);
--> statement-breakpoint
CREATE TABLE `user` (
- `id` text(255) PRIMARY KEY NOT NULL,
- `name` text(255),
- `email` text(255) NOT NULL,
- `emailVerified` integer DEFAULT CURRENT_TIMESTAMP,
- `image` text(255)
+ `id` text PRIMARY KEY NOT NULL,
+ `name` text,
+ `email` text NOT NULL,
+ `emailVerified` integer,
+ `image` text
);
--> statement-breakpoint
CREATE TABLE `verificationToken` (
- `identifier` text(255) NOT NULL,
- `token` text(255) NOT NULL,
+ `identifier` text NOT NULL,
+ `token` text NOT NULL,
`expires` integer NOT NULL,
PRIMARY KEY(`identifier`, `token`)
);
--> statement-breakpoint
-CREATE INDEX `account_userId_idx` ON `account` (`userId`);--> statement-breakpoint
-CREATE INDEX `session_userId_idx` ON `session` (`userId`);--> statement-breakpoint
+CREATE UNIQUE INDEX `authenticator_credentialID_unique` ON `authenticator` (`credentialID`);--> statement-breakpoint
CREATE UNIQUE INDEX `space_name_unique` ON `space` (`name`);--> statement-breakpoint
CREATE INDEX `spaces_name_idx` ON `space` (`name`);--> statement-breakpoint
CREATE INDEX `spaces_user_idx` ON `space` (`user`);--> statement-breakpoint
diff --git a/apps/web/migrations/meta/0000_snapshot.json b/apps/web/migrations/meta/0000_snapshot.json
index 29cc4323..20327dda 100644
--- a/apps/web/migrations/meta/0000_snapshot.json
+++ b/apps/web/migrations/meta/0000_snapshot.json
@@ -1,43 +1,36 @@
{
"version": "6",
"dialect": "sqlite",
- "id": "409cec60-0c4b-4cda-8751-3e70768bbb6c",
+ "id": "4a568d9b-a0e6-44ed-946b-694e34b063f3",
"prevId": "00000000-0000-0000-0000-000000000000",
"tables": {
"account": {
"name": "account",
"columns": {
- "id": {
- "name": "id",
- "type": "integer",
- "primaryKey": true,
- "notNull": true,
- "autoincrement": true
- },
"userId": {
"name": "userId",
- "type": "text(255)",
+ "type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"type": {
"name": "type",
- "type": "text(255)",
+ "type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"provider": {
"name": "provider",
- "type": "text(255)",
+ "type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"providerAccountId": {
"name": "providerAccountId",
- "type": "text(255)",
+ "type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
@@ -65,14 +58,14 @@
},
"token_type": {
"name": "token_type",
- "type": "text(255)",
+ "type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"scope": {
"name": "scope",
- "type": "text(255)",
+ "type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
@@ -86,20 +79,86 @@
},
"session_state": {
"name": "session_state",
- "type": "text(255)",
+ "type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
+ }
+ },
+ "indexes": {},
+ "foreignKeys": {
+ "account_userId_user_id_fk": {
+ "name": "account_userId_user_id_fk",
+ "tableFrom": "account",
+ "tableTo": "user",
+ "columnsFrom": ["userId"],
+ "columnsTo": ["id"],
+ "onDelete": "cascade",
+ "onUpdate": "no action"
+ }
+ },
+ "compositePrimaryKeys": {
+ "account_provider_providerAccountId_pk": {
+ "columns": ["provider", "providerAccountId"],
+ "name": "account_provider_providerAccountId_pk"
+ }
+ },
+ "uniqueConstraints": {}
+ },
+ "authenticator": {
+ "name": "authenticator",
+ "columns": {
+ "credentialID": {
+ "name": "credentialID",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
},
- "oauth_token_secret": {
- "name": "oauth_token_secret",
+ "userId": {
+ "name": "userId",
"type": "text",
"primaryKey": false,
- "notNull": false,
+ "notNull": true,
"autoincrement": false
},
- "oauth_token": {
- "name": "oauth_token",
+ "providerAccountId": {
+ "name": "providerAccountId",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "credentialPublicKey": {
+ "name": "credentialPublicKey",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "counter": {
+ "name": "counter",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "credentialDeviceType": {
+ "name": "credentialDeviceType",
+ "type": "text",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "credentialBackedUp": {
+ "name": "credentialBackedUp",
+ "type": "integer",
+ "primaryKey": false,
+ "notNull": true,
+ "autoincrement": false
+ },
+ "transports": {
+ "name": "transports",
"type": "text",
"primaryKey": false,
"notNull": false,
@@ -107,16 +166,16 @@
}
},
"indexes": {
- "account_userId_idx": {
- "name": "account_userId_idx",
- "columns": ["userId"],
- "isUnique": false
+ "authenticator_credentialID_unique": {
+ "name": "authenticator_credentialID_unique",
+ "columns": ["credentialID"],
+ "isUnique": true
}
},
"foreignKeys": {
- "account_userId_user_id_fk": {
- "name": "account_userId_user_id_fk",
- "tableFrom": "account",
+ "authenticator_userId_user_id_fk": {
+ "name": "authenticator_userId_user_id_fk",
+ "tableFrom": "authenticator",
"tableTo": "user",
"columnsFrom": ["userId"],
"columnsTo": ["id"],
@@ -124,7 +183,12 @@
"onUpdate": "no action"
}
},
- "compositePrimaryKeys": {},
+ "compositePrimaryKeys": {
+ "authenticator_userId_credentialID_pk": {
+ "columns": ["credentialID", "userId"],
+ "name": "authenticator_userId_credentialID_pk"
+ }
+ },
"uniqueConstraints": {}
},
"contentToSpace": {
@@ -177,23 +241,16 @@
"session": {
"name": "session",
"columns": {
- "id": {
- "name": "id",
- "type": "integer",
- "primaryKey": true,
- "notNull": true,
- "autoincrement": true
- },
"sessionToken": {
"name": "sessionToken",
- "type": "text(255)",
- "primaryKey": false,
+ "type": "text",
+ "primaryKey": true,
"notNull": true,
"autoincrement": false
},
"userId": {
"name": "userId",
- "type": "text(255)",
+ "type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
@@ -206,13 +263,7 @@
"autoincrement": false
}
},
- "indexes": {
- "session_userId_idx": {
- "name": "session_userId_idx",
- "columns": ["userId"],
- "isUnique": false
- }
- },
+ "indexes": {},
"foreignKeys": {
"session_userId_user_id_fk": {
"name": "session_userId_user_id_fk",
@@ -360,7 +411,7 @@
},
"user": {
"name": "user",
- "type": "text(255)",
+ "type": "integer",
"primaryKey": false,
"notNull": false,
"autoincrement": false
@@ -407,21 +458,21 @@
"columns": {
"id": {
"name": "id",
- "type": "text(255)",
+ "type": "text",
"primaryKey": true,
"notNull": true,
"autoincrement": false
},
"name": {
"name": "name",
- "type": "text(255)",
+ "type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
},
"email": {
"name": "email",
- "type": "text(255)",
+ "type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
@@ -431,12 +482,11 @@
"type": "integer",
"primaryKey": false,
"notNull": false,
- "autoincrement": false,
- "default": "CURRENT_TIMESTAMP"
+ "autoincrement": false
},
"image": {
"name": "image",
- "type": "text(255)",
+ "type": "text",
"primaryKey": false,
"notNull": false,
"autoincrement": false
@@ -452,14 +502,14 @@
"columns": {
"identifier": {
"name": "identifier",
- "type": "text(255)",
+ "type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
},
"token": {
"name": "token",
- "type": "text(255)",
+ "type": "text",
"primaryKey": false,
"notNull": true,
"autoincrement": false
diff --git a/apps/web/migrations/meta/_journal.json b/apps/web/migrations/meta/_journal.json
index a77d9616..90bb9df7 100644
--- a/apps/web/migrations/meta/_journal.json
+++ b/apps/web/migrations/meta/_journal.json
@@ -5,8 +5,8 @@
{
"idx": 0,
"version": "6",
- "when": 1716677954608,
- "tag": "0000_calm_monster_badoon",
+ "when": 1718412145023,
+ "tag": "0000_absurd_pandemic",
"breakpoints": true
}
]
diff --git a/apps/web/next.config.mjs b/apps/web/next.config.mjs
index 51c492f4..cc820fef 100644
--- a/apps/web/next.config.mjs
+++ b/apps/web/next.config.mjs
@@ -4,6 +4,7 @@ import { setupDevPlatform } from "@cloudflare/next-on-pages/next-dev";
/** @type {import('next').NextConfig} */
const nextConfig = {
transpilePackages: ["@repo/ui"],
+ reactStrictMode: false,
};
export default MillionLint.next({
rsc: true,
diff --git a/apps/web/package.json b/apps/web/package.json
index 0bfb4119..19f77187 100644
--- a/apps/web/package.json
+++ b/apps/web/package.json
@@ -10,7 +10,9 @@
"cf-typegen": "wrangler types --env-interface CloudflareEnv env.d.ts",
"pages:build": "bunx @cloudflare/next-on-pages",
"preview": "bun pages:build && wrangler pages dev",
- "deploy": "bun pages:build && wrangler pages deploy"
+ "deploy": "bun pages:build && wrangler pages deploy",
+ "schema-update": "bunx drizzle-kit generate sqlite",
+ "update-local-db": "bunx wrangler d1 execute dev-d1-anycontext --local"
},
"dependencies": {
"@million/lint": "^1.0.0-rc.11",
@@ -18,11 +20,15 @@
"@radix-ui/react-popover": "^1.0.7",
"@repo/ui": "*",
"cmdk": "^1.0.0",
+ "lowlight": "^3.1.0",
"million": "^3.1.6",
"next": "^14.1.1",
+ "novel": "^0.4.2",
"nuqs": "^1.17.4",
"react": "^18.2.0",
- "react-dom": "^18.2.0"
+ "react-dom": "^18.2.0",
+ "react-resizable-panels": "^2.0.19",
+ "use-debounce": "^10.0.1"
},
"devDependencies": {
"@next/eslint-plugin-next": "^14.1.1",
diff --git a/apps/web/public/embed-icons/codepen.png b/apps/web/public/embed-icons/codepen.png
new file mode 100644
index 00000000..bd47194f
--- /dev/null
+++ b/apps/web/public/embed-icons/codepen.png
Binary files differ
diff --git a/apps/web/public/embed-icons/codesandbox.png b/apps/web/public/embed-icons/codesandbox.png
new file mode 100644
index 00000000..5fe29a73
--- /dev/null
+++ b/apps/web/public/embed-icons/codesandbox.png
Binary files differ
diff --git a/apps/web/public/embed-icons/desmos.png b/apps/web/public/embed-icons/desmos.png
new file mode 100644
index 00000000..28000c63
--- /dev/null
+++ b/apps/web/public/embed-icons/desmos.png
Binary files differ
diff --git a/apps/web/public/embed-icons/excalidraw.png b/apps/web/public/embed-icons/excalidraw.png
new file mode 100644
index 00000000..fad01b47
--- /dev/null
+++ b/apps/web/public/embed-icons/excalidraw.png
Binary files differ
diff --git a/apps/web/public/embed-icons/felt.png b/apps/web/public/embed-icons/felt.png
new file mode 100644
index 00000000..e4d94a62
--- /dev/null
+++ b/apps/web/public/embed-icons/felt.png
Binary files differ
diff --git a/apps/web/public/embed-icons/figma.png b/apps/web/public/embed-icons/figma.png
new file mode 100644
index 00000000..399b965b
--- /dev/null
+++ b/apps/web/public/embed-icons/figma.png
Binary files differ
diff --git a/apps/web/public/embed-icons/github_gist.png b/apps/web/public/embed-icons/github_gist.png
new file mode 100644
index 00000000..4bc21276
--- /dev/null
+++ b/apps/web/public/embed-icons/github_gist.png
Binary files differ
diff --git a/apps/web/public/embed-icons/google_calendar.png b/apps/web/public/embed-icons/google_calendar.png
new file mode 100644
index 00000000..758f8af8
--- /dev/null
+++ b/apps/web/public/embed-icons/google_calendar.png
Binary files differ
diff --git a/apps/web/public/embed-icons/google_maps.png b/apps/web/public/embed-icons/google_maps.png
new file mode 100644
index 00000000..b9422c4c
--- /dev/null
+++ b/apps/web/public/embed-icons/google_maps.png
Binary files differ
diff --git a/apps/web/public/embed-icons/google_slides.png b/apps/web/public/embed-icons/google_slides.png
new file mode 100644
index 00000000..818e1018
--- /dev/null
+++ b/apps/web/public/embed-icons/google_slides.png
Binary files differ
diff --git a/apps/web/public/embed-icons/observable.png b/apps/web/public/embed-icons/observable.png
new file mode 100644
index 00000000..e86f8a1d
--- /dev/null
+++ b/apps/web/public/embed-icons/observable.png
Binary files differ
diff --git a/apps/web/public/embed-icons/replit.png b/apps/web/public/embed-icons/replit.png
new file mode 100644
index 00000000..9c26ae73
--- /dev/null
+++ b/apps/web/public/embed-icons/replit.png
Binary files differ
diff --git a/apps/web/public/embed-icons/scratch.png b/apps/web/public/embed-icons/scratch.png
new file mode 100644
index 00000000..9be22a02
--- /dev/null
+++ b/apps/web/public/embed-icons/scratch.png
Binary files differ
diff --git a/apps/web/public/embed-icons/spotify.png b/apps/web/public/embed-icons/spotify.png
new file mode 100644
index 00000000..9e888dc2
--- /dev/null
+++ b/apps/web/public/embed-icons/spotify.png
Binary files differ
diff --git a/apps/web/public/embed-icons/tldraw.png b/apps/web/public/embed-icons/tldraw.png
new file mode 100644
index 00000000..eb38d1f7
--- /dev/null
+++ b/apps/web/public/embed-icons/tldraw.png
Binary files differ
diff --git a/apps/web/public/embed-icons/val_town.png b/apps/web/public/embed-icons/val_town.png
new file mode 100644
index 00000000..90f71857
--- /dev/null
+++ b/apps/web/public/embed-icons/val_town.png
Binary files differ
diff --git a/apps/web/public/embed-icons/vimeo.png b/apps/web/public/embed-icons/vimeo.png
new file mode 100644
index 00000000..a0e361c7
--- /dev/null
+++ b/apps/web/public/embed-icons/vimeo.png
Binary files differ
diff --git a/apps/web/public/embed-icons/youtube.png b/apps/web/public/embed-icons/youtube.png
new file mode 100644
index 00000000..26bf3334
--- /dev/null
+++ b/apps/web/public/embed-icons/youtube.png
Binary files differ
diff --git a/apps/web/public/fonts/IBMPlexMono-Medium.woff2 b/apps/web/public/fonts/IBMPlexMono-Medium.woff2
new file mode 100644
index 00000000..637df267
--- /dev/null
+++ b/apps/web/public/fonts/IBMPlexMono-Medium.woff2
Binary files differ
diff --git a/apps/web/public/fonts/IBMPlexSans-Medium.woff2 b/apps/web/public/fonts/IBMPlexSans-Medium.woff2
new file mode 100644
index 00000000..0e32d371
--- /dev/null
+++ b/apps/web/public/fonts/IBMPlexSans-Medium.woff2
Binary files differ
diff --git a/apps/web/public/fonts/IBMPlexSerif-Medium.woff2 b/apps/web/public/fonts/IBMPlexSerif-Medium.woff2
new file mode 100644
index 00000000..829423bd
--- /dev/null
+++ b/apps/web/public/fonts/IBMPlexSerif-Medium.woff2
Binary files differ
diff --git a/apps/web/public/fonts/Shantell_Sans-Tldrawish.woff2 b/apps/web/public/fonts/Shantell_Sans-Tldrawish.woff2
new file mode 100644
index 00000000..7a50fc98
--- /dev/null
+++ b/apps/web/public/fonts/Shantell_Sans-Tldrawish.woff2
Binary files differ
diff --git a/apps/web/public/icons/icon/align-bottom.svg b/apps/web/public/icons/icon/align-bottom.svg
new file mode 100644
index 00000000..a6933e43
--- /dev/null
+++ b/apps/web/public/icons/icon/align-bottom.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M18 5C18 4.44772 17.5523 4 17 4H13C12.4477 4 12 4.44772 12 5L12 28H3C2.44772 28 2 28.4477 2 29C2 29.5523 2.44772 30 3 30L27 30C27.5523 30 28 29.5523 28 29C28 28.4477 27.5523 28 27 28H18L18 5Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/align-center-horizontal.svg b/apps/web/public/icons/icon/align-center-horizontal.svg
new file mode 100644
index 00000000..1fe337d2
--- /dev/null
+++ b/apps/web/public/icons/icon/align-center-horizontal.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M1.99976 13C1.99976 12.4477 2.44747 12 2.99976 12H26.9998C27.552 12 27.9998 12.4477 27.9998 13V17C27.9998 17.5523 27.552 18 26.9998 18H2.99976C2.44747 18 1.99976 17.5523 1.99976 17V13Z" fill="black"/>
+<path d="M13.9998 3C13.9998 2.44772 14.4475 2 14.9998 2C15.552 2 15.9998 2.44772 15.9998 3V27C15.9998 27.5523 15.552 28 14.9998 28C14.4475 28 13.9998 27.5523 13.9998 27V3Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/align-center-vertical.svg b/apps/web/public/icons/icon/align-center-vertical.svg
new file mode 100644
index 00000000..e0095c30
--- /dev/null
+++ b/apps/web/public/icons/icon/align-center-vertical.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M12.9998 2C12.4475 2 11.9998 2.44772 11.9998 3V14H2.99976C2.44747 14 1.99976 14.4477 1.99976 15C1.99976 15.5523 2.44747 16 2.99976 16H11.9998V27C11.9998 27.5523 12.4475 28 12.9998 28H16.9998C17.552 28 17.9998 27.5523 17.9998 27V16H26.9998C27.552 16 27.9998 15.5523 27.9998 15C27.9998 14.4477 27.552 14 26.9998 14H17.9998V3C17.9998 2.44772 17.552 2 16.9998 2H12.9998Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/align-left.svg b/apps/web/public/icons/icon/align-left.svg
new file mode 100644
index 00000000..bc77f383
--- /dev/null
+++ b/apps/web/public/icons/icon/align-left.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M0 3C0 2.44772 0.447715 2 1 2C1.55228 2 2 2.44772 2 3V27C2 27.5523 1.55228 28 1 28C0.447715 28 0 27.5523 0 27V3Z" fill="black"/>
+<path d="M0 12H25C25.5523 12 26 12.4477 26 13V17C26 17.5523 25.5523 18 25 18H0V12Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/align-right.svg b/apps/web/public/icons/icon/align-right.svg
new file mode 100644
index 00000000..65670422
--- /dev/null
+++ b/apps/web/public/icons/icon/align-right.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M3.99976 13C3.99976 12.4477 4.44747 12 4.99976 12H29.9998V18H4.99976C4.44747 18 3.99976 17.5523 3.99976 17V13Z" fill="black"/>
+<path d="M27.9998 3C27.9998 2.44772 28.4475 2 28.9998 2C29.552 2 29.9998 2.44772 29.9998 3V27C29.9998 27.5523 29.552 28 28.9998 28C28.4475 28 27.9998 27.5523 27.9998 27V3Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/align-top.svg b/apps/web/public/icons/icon/align-top.svg
new file mode 100644
index 00000000..06cefbc4
--- /dev/null
+++ b/apps/web/public/icons/icon/align-top.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M2.99988 1.52588e-05C2.44759 1.52588e-05 1.99988 0.447731 1.99988 1.00002C1.99988 1.5523 2.44759 2.00002 2.99988 2.00002H11.9999V25C11.9999 25.5523 12.4476 26 12.9999 26H16.9999C17.5522 26 17.9999 25.5523 17.9999 25V2.00002H26.9999C27.5522 2.00002 27.9999 1.5523 27.9999 1.00002C27.9999 0.447731 27.5522 1.52588e-05 26.9999 1.52588e-05H2.99988Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/arrow-left.svg b/apps/web/public/icons/icon/arrow-left.svg
new file mode 100644
index 00000000..5567a7c3
--- /dev/null
+++ b/apps/web/public/icons/icon/arrow-left.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M12.4996 21.5001L5.99963 15.0001M5.99963 15.0001L12.4996 8.50012M5.99963 15.0001L23.9999 15.0001" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/arrowhead-arrow.svg b/apps/web/public/icons/icon/arrowhead-arrow.svg
new file mode 100644
index 00000000..cffa6149
--- /dev/null
+++ b/apps/web/public/icons/icon/arrowhead-arrow.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M1 15L27 15M13 26L29 15L13 4" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/arrowhead-bar.svg b/apps/web/public/icons/icon/arrowhead-bar.svg
new file mode 100644
index 00000000..114cc433
--- /dev/null
+++ b/apps/web/public/icons/icon/arrowhead-bar.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M1 15L29 15M29 15V2M29 15V28" stroke="black" stroke-width="2" stroke-linecap="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/arrowhead-diamond.svg b/apps/web/public/icons/icon/arrowhead-diamond.svg
new file mode 100644
index 00000000..ec9b5d6f
--- /dev/null
+++ b/apps/web/public/icons/icon/arrowhead-diamond.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M18.4142 3.82822L28.3137 13.7277C29.0948 14.5088 29.0948 15.7751 28.3137 16.5561L18.4142 26.4556C17.6332 27.2367 16.3669 27.2367 15.5858 26.4556L5.68631 16.5561C4.90526 15.7751 4.90526 14.5088 5.68631 13.7277L15.5858 3.82822C16.3669 3.04717 17.6332 3.04717 18.4142 3.82822Z" stroke="black" stroke-width="2"/>
+<path d="M1 15H5" stroke="black" stroke-width="2" stroke-linecap="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/arrowhead-dot.svg b/apps/web/public/icons/icon/arrowhead-dot.svg
new file mode 100644
index 00000000..e693d90f
--- /dev/null
+++ b/apps/web/public/icons/icon/arrowhead-dot.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M29 15C29 21.0751 24.0751 26 18 26C11.9249 26 7 21.0751 7 15C7 8.92487 11.9249 4 18 4C24.0751 4 29 8.92487 29 15Z" stroke="black" stroke-width="2"/>
+<path d="M1 15H6" stroke="black" stroke-width="2" stroke-linecap="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/arrowhead-none.svg b/apps/web/public/icons/icon/arrowhead-none.svg
new file mode 100644
index 00000000..268c5f7b
--- /dev/null
+++ b/apps/web/public/icons/icon/arrowhead-none.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M1 15L29 15" stroke="black" stroke-width="2" stroke-linecap="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/arrowhead-square.svg b/apps/web/public/icons/icon/arrowhead-square.svg
new file mode 100644
index 00000000..2ed2383c
--- /dev/null
+++ b/apps/web/public/icons/icon/arrowhead-square.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M1 15L5 15M8 27H26C27.6569 27 29 25.6569 29 24V6C29 4.34315 27.6569 3 26 3H8C6.34315 3 5 4.34315 5 6V24C5 25.6569 6.34315 27 8 27Z" stroke="black" stroke-width="2" stroke-linecap="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/arrowhead-triangle-inverted.svg b/apps/web/public/icons/icon/arrowhead-triangle-inverted.svg
new file mode 100644
index 00000000..d927492d
--- /dev/null
+++ b/apps/web/public/icons/icon/arrowhead-triangle-inverted.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M1 14C0.447715 14 0 14.4477 0 15C5.96046e-08 15.5523 0.447715 16 1 16L1 14ZM29 2H30C30 1.62447 29.7896 1.2806 29.4553 1.10964C29.1209 0.938677 28.7189 0.969452 28.4145 1.18932L29 2ZM29 28L28.4145 28.8107C28.7189 29.0305 29.1209 29.0613 29.4553 28.8904C29.7896 28.7194 30 28.3755 30 28H29ZM1 16L11 16V14L1 14L1 16ZM28 2V28H30V2H28ZM29.5855 27.1893L11.5855 14.1893L10.4145 15.8107L28.4145 28.8107L29.5855 27.1893ZM11.5855 15.8107L29.5855 2.81068L28.4145 1.18932L10.4145 14.1893L11.5855 15.8107Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/arrowhead-triangle.svg b/apps/web/public/icons/icon/arrowhead-triangle.svg
new file mode 100644
index 00000000..42721afd
--- /dev/null
+++ b/apps/web/public/icons/icon/arrowhead-triangle.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M1 15.1539L11.6923 15.1539M12.6923 3.46155V26.5385L28.8462 15L12.6923 3.46155Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/blob.svg b/apps/web/public/icons/icon/blob.svg
new file mode 100644
index 00000000..1a57e75a
--- /dev/null
+++ b/apps/web/public/icons/icon/blob.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M1.98125 12.4723C1.83836 12.6938 2.09551 13.0008 2.33213 12.8846C3.14685 12.4842 3.93568 12.0619 4.70733 11.5967C4.93448 11.4598 5.18303 11.7046 5.0359 11.9253C3.13151 14.7818 -1.59634 21.7981 3.60918 22.2578C6.76899 22.5368 9.93894 19.9616 12.9133 17.2735C13.1318 17.0759 13.4376 17.335 13.2743 17.5802C10.8537 21.2143 6.41515 28.636 12.2493 28.99C17.3234 29.298 16.1533 24.5977 27.679 18.2524C28.5169 17.7911 28.7805 16.8396 28.5707 15.9125C28.1988 13.9184 24.4364 13.9496 22.3466 14.4984C22.0925 14.5651 21.8477 14.245 22.0182 14.045C24.5643 11.0592 31.8711 3.60414 27.2697 1.27741C23.219 -0.770836 13.918 8.89819 9.96535 12.5406C9.75706 12.7325 9.52989 12.5192 9.69793 12.2912C11.6423 9.65308 14.1864 6.73192 14.6429 3.69364C14.7073 3.26555 14.628 2.86103 14.4053 2.48008C14.1825 2.09913 13.8587 1.8145 13.4339 1.6262C9.1247 -0.284007 4.20268 9.02761 1.98125 12.4723Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/bring-forward.svg b/apps/web/public/icons/icon/bring-forward.svg
new file mode 100644
index 00000000..57040883
--- /dev/null
+++ b/apps/web/public/icons/icon/bring-forward.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M8.50024 12.6388L15.0002 6.5M15.0002 6.5L21.5002 12.6388M15.0002 6.5L15.0002 23.5" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/bring-to-front.svg b/apps/web/public/icons/icon/bring-to-front.svg
new file mode 100644
index 00000000..2e4236d6
--- /dev/null
+++ b/apps/web/public/icons/icon/bring-to-front.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M8.50024 15.4998L15.0002 8.99976M15.0002 8.99976L21.5002 15.4998M15.0002 8.99976L15.0002 27M3 3L27 3" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/broken.svg b/apps/web/public/icons/icon/broken.svg
new file mode 100644
index 00000000..bc893659
--- /dev/null
+++ b/apps/web/public/icons/icon/broken.svg
@@ -0,0 +1,5 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M28.0001 12C28.0001 11.4477 27.5524 11 27.0001 11C26.4478 11 26.0001 11.4477 26.0001 12V26H12C11.4477 26 11 26.4477 11 27C11 27.5523 11.4477 28 12 28H26.9C27.4523 28 28.0001 27.4522 28.0001 26.8999V12Z" fill="black"/>
+<path d="M2 18C2 18.5523 2.44769 19 2.99997 19C3.55225 19 4 18.5523 4 18L4 4L18 4C18.5523 4 19 3.55228 19 3C19 2.44772 18.5523 2 18 2L3.10007 2C2.54779 2 2 2.54781 2 3.10009L2 18Z" fill="black"/>
+<rect x="1.58411" y="27.0018" width="35.9461" height="2" rx="1" transform="rotate(-45 1.58411 27.0018)" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/check-circle.svg b/apps/web/public/icons/icon/check-circle.svg
new file mode 100644
index 00000000..67c9b863
--- /dev/null
+++ b/apps/web/public/icons/icon/check-circle.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M14.9998 27.2949C21.7904 27.2949 27.2954 21.7899 27.2954 14.9993C27.2954 8.20858 21.7904 2.70361 14.9998 2.70361C8.20907 2.70361 2.7041 8.20858 2.7041 14.9993C2.7041 21.7899 8.20907 27.2949 14.9998 27.2949Z" stroke="black" stroke-width="2"/>
+<path d="M20.7075 9.39489C21.1367 9.67551 21.2572 10.2509 20.9765 10.6801L14.6629 20.3363C14.5141 20.5639 14.273 20.7148 14.0033 20.7492C13.7336 20.7836 13.4624 20.6981 13.2612 20.5152L9.17587 16.8013C8.79644 16.4564 8.76848 15.8691 9.11342 15.4897C9.45835 15.1103 10.0456 15.0823 10.425 15.4273L13.7046 18.4087L19.4223 9.66389C19.7029 9.2347 20.2784 9.11426 20.7075 9.39489Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/check.svg b/apps/web/public/icons/icon/check.svg
new file mode 100644
index 00000000..86caad79
--- /dev/null
+++ b/apps/web/public/icons/icon/check.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M22.9339 7.45369C23.5118 7.83148 23.6739 8.60615 23.2961 9.18396L14.7961 22.184C14.5958 22.4903 14.2712 22.6935 13.9082 22.7398C13.5451 22.7862 13.1799 22.6711 12.909 22.4248L7.40905 17.4248C6.89822 16.9604 6.86058 16.1699 7.32496 15.6591C7.78935 15.1482 8.57991 15.1106 9.09073 15.575L13.506 19.5888L21.2037 7.81584C21.5815 7.23803 22.3561 7.07589 22.9339 7.45369Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/chevron-down.svg b/apps/web/public/icons/icon/chevron-down.svg
new file mode 100644
index 00000000..f43179ca
--- /dev/null
+++ b/apps/web/public/icons/icon/chevron-down.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M6.27047 12.3161C6.6482 11.9131 7.28103 11.8927 7.68394 12.2705L15 19.1293L22.3161 12.2705C22.719 11.8927 23.3518 11.9131 23.7295 12.3161C24.1073 12.719 24.0869 13.3518 23.6839 13.7295L15.6839 21.2295C15.2993 21.5902 14.7007 21.5902 14.3161 21.2295L6.31606 13.7295C5.91315 13.3518 5.89274 12.719 6.27047 12.3161Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/chevron-left.svg b/apps/web/public/icons/icon/chevron-left.svg
new file mode 100644
index 00000000..7fba4b06
--- /dev/null
+++ b/apps/web/public/icons/icon/chevron-left.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M17.6836 6.27028C18.0865 6.64801 18.1069 7.28085 17.7292 7.68376L10.8704 14.9998L17.7292 22.3159C18.1069 22.7188 18.0865 23.3516 17.6836 23.7294C17.2807 24.1071 16.6478 24.0867 16.2701 23.6838L8.7701 15.6838C8.40948 15.2991 8.40948 14.7005 8.7701 14.3159L16.2701 6.31588C16.6478 5.91297 17.2807 5.89255 17.6836 6.27028Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/chevron-right.svg b/apps/web/public/icons/icon/chevron-right.svg
new file mode 100644
index 00000000..957f0ec6
--- /dev/null
+++ b/apps/web/public/icons/icon/chevron-right.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12.3168 6.27013C12.7197 5.8924 13.3525 5.91281 13.7303 6.31573L21.2303 14.3157C21.5909 14.7004 21.5909 15.299 21.2303 15.6836L13.7303 23.6836C13.3525 24.0865 12.7197 24.1069 12.3168 23.7292C11.9139 23.3515 11.8935 22.7186 12.2712 22.3157L19.13 14.9997L12.2712 7.68361C11.8935 7.2807 11.9139 6.64786 12.3168 6.27013Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/chevron-up.svg b/apps/web/public/icons/icon/chevron-up.svg
new file mode 100644
index 00000000..d44443df
--- /dev/null
+++ b/apps/web/public/icons/icon/chevron-up.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M6.27047 17.6839C6.6482 18.0869 7.28103 18.1073 7.68394 17.7295L15 10.8707L22.3161 17.7295C22.719 18.1073 23.3518 18.0869 23.7295 17.6839C24.1073 17.281 24.0869 16.6482 23.6839 16.2705L15.6839 8.77046C15.2993 8.40984 14.7007 8.40984 14.3161 8.77046L6.31606 16.2705C5.91315 16.6482 5.89274 17.281 6.27047 17.6839Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/chevrons-ne.svg b/apps/web/public/icons/icon/chevrons-ne.svg
new file mode 100644
index 00000000..3748fdc8
--- /dev/null
+++ b/apps/web/public/icons/icon/chevrons-ne.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M7.5271 8.73255L13.7947 15.0002L7.52722 21.2677M16.2052 8.73242L22.4728 15L16.2053 21.2675" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/chevrons-sw.svg b/apps/web/public/icons/icon/chevrons-sw.svg
new file mode 100644
index 00000000..0773322b
--- /dev/null
+++ b/apps/web/public/icons/icon/chevrons-sw.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M22.4728 21.2675L16.2052 14.9999L22.4727 8.73242M13.7947 21.2676L7.5271 15L13.7946 8.73254" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/clipboard-copied.svg b/apps/web/public/icons/icon/clipboard-copied.svg
new file mode 100644
index 00000000..5bc86e6b
--- /dev/null
+++ b/apps/web/public/icons/icon/clipboard-copied.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M8 2V4H18V2H8ZM6 1.5C6 0.671573 6.67157 0 7.5 0H18.5C19.3284 0 20 0.671572 20 1.5V2H21C22.6569 2 24 3.34315 24 5V14H22V5C22 4.44772 21.5523 4 21 4H20V4.5C20 5.32843 19.3284 6 18.5 6H7.5C6.67157 6 6 5.32843 6 4.5V4H5C4.44771 4 4 4.44772 4 5V25C4 25.5523 4.44772 26 5 26H12V28H5C3.34315 28 2 26.6569 2 25V5C2 3.34314 3.34315 2 5 2H6V1.5Z" fill="black"/>
+<path d="M27.5197 17.173C28.0099 17.4936 28.1475 18.1509 27.827 18.6411L20.6149 29.6713C20.445 29.9313 20.1696 30.1037 19.8615 30.143C19.5534 30.1823 19.2436 30.0846 19.0138 29.8757L14.3472 25.6333C13.9137 25.2393 13.8818 24.5685 14.2758 24.1351C14.6698 23.7017 15.3406 23.6697 15.774 24.0638L19.5203 27.4694L26.0516 17.4803C26.3721 16.9901 27.0294 16.8525 27.5197 17.173Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/clipboard-copy.svg b/apps/web/public/icons/icon/clipboard-copy.svg
new file mode 100644
index 00000000..b0861dbb
--- /dev/null
+++ b/apps/web/public/icons/icon/clipboard-copy.svg
@@ -0,0 +1,15 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M8 2V4H18V2H8ZM6 1.5C6 0.671573 6.67157 0 7.5 0H18.5C19.3284 0 20 0.671572 20 1.5V2H21C22.6569 2 24 3.34315 24 5V14H22V5C22 4.44772 21.5523 4 21 4H20V4.5C20 5.32843 19.3284 6 18.5 6H7.5C6.67157 6 6 5.32843 6 4.5V4H5C4.44771 4 4 4.44772 4 5V25C4 25.5523 4.44772 26 5 26H12V28H5C3.34315 28 2 26.6569 2 25V5C2 3.34314 3.34315 2 5 2H6V1.5Z" fill="black"/>
+<path d="M28 29C28 29.5523 27.5523 30 27 30C26.4477 30 26 29.5523 26 29C26 28.4477 26.4477 28 27 28C27.5523 28 28 28.4477 28 29Z" fill="black"/>
+<path d="M28 25C28 25.5523 27.5523 26 27 26C26.4477 26 26 25.5523 26 25C26 24.4477 26.4477 24 27 24C27.5523 24 28 24.4477 28 25Z" fill="black"/>
+<path d="M28 21C28 21.5523 27.5523 22 27 22C26.4477 22 26 21.5523 26 21C26 20.4477 26.4477 20 27 20C27.5523 20 28 20.4477 28 21Z" fill="black"/>
+<path d="M28 17C28 17.5523 27.5523 18 27 18C26.4477 18 26 17.5523 26 17C26 16.4477 26.4477 16 27 16C27.5523 16 28 16.4477 28 17Z" fill="black"/>
+<path d="M24 17C24 17.5523 23.5523 18 23 18C22.4477 18 22 17.5523 22 17C22 16.4477 22.4477 16 23 16C23.5523 16 24 16.4477 24 17Z" fill="black"/>
+<path d="M20 17C20 17.5523 19.5523 18 19 18C18.4477 18 18 17.5523 18 17C18 16.4477 18.4477 16 19 16C19.5523 16 20 16.4477 20 17Z" fill="black"/>
+<path d="M16 17C16 17.5523 15.5523 18 15 18C14.4477 18 14 17.5523 14 17C14 16.4477 14.4477 16 15 16C15.5523 16 16 16.4477 16 17Z" fill="black"/>
+<path d="M16 21C16 21.5523 15.5523 22 15 22C14.4477 22 14 21.5523 14 21C14 20.4477 14.4477 20 15 20C15.5523 20 16 20.4477 16 21Z" fill="black"/>
+<path d="M16 25C16 25.5523 15.5523 26 15 26C14.4477 26 14 25.5523 14 25C14 24.4477 14.4477 24 15 24C15.5523 24 16 24.4477 16 25Z" fill="black"/>
+<path d="M16 29C16 29.5523 15.5523 30 15 30C14.4477 30 14 29.5523 14 29C14 28.4477 14.4477 28 15 28C15.5523 28 16 28.4477 16 29Z" fill="black"/>
+<path d="M20 29C20 29.5523 19.5523 30 19 30C18.4477 30 18 29.5523 18 29C18 28.4477 18.4477 28 19 28C19.5523 28 20 28.4477 20 29Z" fill="black"/>
+<path d="M24 29C24 29.5523 23.5523 30 23 30C22.4477 30 22 29.5523 22 29C22 28.4477 22.4477 28 23 28C23.5523 28 24 28.4477 24 29Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/color.svg b/apps/web/public/icons/icon/color.svg
new file mode 100644
index 00000000..d328079f
--- /dev/null
+++ b/apps/web/public/icons/icon/color.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="15" cy="15" r="13" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/cross-2.svg b/apps/web/public/icons/icon/cross-2.svg
new file mode 100644
index 00000000..e97b8bd5
--- /dev/null
+++ b/apps/web/public/icons/icon/cross-2.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M23.5631 8.06315C24.0123 7.61405 24.0123 6.88591 23.5631 6.4368C23.114 5.9877 22.3859 5.9877 21.9368 6.4368L15 13.3736L8.06315 6.4368C7.61405 5.9877 6.88591 5.9877 6.4368 6.4368C5.9877 6.88591 5.9877 7.61405 6.4368 8.06315L13.3736 15L6.4368 21.9368C5.9877 22.3859 5.9877 23.114 6.4368 23.5631C6.88591 24.0123 7.61405 24.0123 8.06315 23.5631L15 16.6263L21.9368 23.5631C22.3859 24.0123 23.114 24.0123 23.5631 23.5631C24.0123 23.114 24.0123 22.3859 23.5631 21.9368L16.6263 15L23.5631 8.06315Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/cross-circle.svg b/apps/web/public/icons/icon/cross-circle.svg
new file mode 100644
index 00000000..3e3dbc85
--- /dev/null
+++ b/apps/web/public/icons/icon/cross-circle.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M19.4422 11.8925C19.8106 11.524 19.8106 10.9264 19.4422 10.5579C19.0736 10.1894 18.4761 10.1894 18.1076 10.5579L15 13.6654L11.8925 10.5579C11.524 10.1894 10.9264 10.1894 10.5579 10.5579C10.1894 10.9264 10.1894 11.524 10.5579 11.8925L13.6654 15L10.5579 18.1075C10.1894 18.4761 10.1894 19.0736 10.5579 19.4421C10.9264 19.8107 11.524 19.8107 11.8925 19.4421L15 16.3346L18.1076 19.4421C18.4761 19.8107 19.0736 19.8107 19.4422 19.4421C19.8106 19.0736 19.8106 18.4761 19.4422 18.1075L16.3346 15L19.4422 11.8925Z" fill="black"/>
+<path d="M14.9998 27.2949C21.7904 27.2949 27.2954 21.7899 27.2954 14.9993C27.2954 8.20858 21.7904 2.70361 14.9998 2.70361C8.20907 2.70361 2.7041 8.20858 2.7041 14.9993C2.7041 21.7899 8.20907 27.2949 14.9998 27.2949Z" stroke="black" stroke-width="2"/>
+</svg>
diff --git a/apps/web/public/icons/icon/dash-dashed.svg b/apps/web/public/icons/icon/dash-dashed.svg
new file mode 100644
index 00000000..f51ce9cc
--- /dev/null
+++ b/apps/web/public/icons/icon/dash-dashed.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M12.4647 2.24741C13.2859 2.08493 14.1339 2 15 2C15.8661 2 16.7141 2.08493 17.5353 2.24741C18.4079 2.42002 18.9753 3.26726 18.8026 4.13978C18.63 5.01229 17.7828 5.57968 16.9103 5.40707C16.2937 5.2851 15.6552 5.2209 15 5.2209C14.3448 5.2209 13.7063 5.2851 13.0897 5.40707C12.2172 5.57968 11.37 5.01229 11.1974 4.13978C11.0248 3.26726 11.5921 2.42002 12.4647 2.24741ZM10.01 4.63292C10.5048 5.372 10.3068 6.37227 9.56771 6.86708C8.50059 7.5815 7.5815 8.50059 6.86708 9.56771C6.37227 10.3068 5.372 10.5048 4.63292 10.01C3.89383 9.51521 3.69581 8.51495 4.19061 7.77586C5.13925 6.35889 6.35889 5.13925 7.77586 4.19061C8.51495 3.69581 9.51521 3.89383 10.01 4.63292ZM19.99 4.63292C20.4848 3.89383 21.4851 3.69581 22.2241 4.19061C23.6411 5.13925 24.8607 6.35889 25.8094 7.77586C26.3042 8.51495 26.1062 9.51521 25.3671 10.01C24.628 10.5048 23.6277 10.3068 23.1329 9.56771C22.4185 8.50059 21.4994 7.5815 20.4323 6.86708C19.6932 6.37227 19.4952 5.372 19.99 4.63292ZM4.13978 11.1974C5.01229 11.37 5.57968 12.2172 5.40707 13.0897C5.2851 13.7063 5.2209 14.3448 5.2209 15C5.2209 15.6552 5.2851 16.2937 5.40707 16.9103C5.57968 17.7828 5.01229 18.63 4.13978 18.8026C3.26726 18.9752 2.42002 18.4079 2.24741 17.5353C2.08493 16.7141 2 15.8661 2 15C2 14.1339 2.08493 13.2859 2.24741 12.4647C2.42002 11.5921 3.26726 11.0247 4.13978 11.1974ZM25.8602 11.1974C26.7327 11.0248 27.58 11.5921 27.7526 12.4647C27.9151 13.2859 28 14.1339 28 15C28 15.8661 27.9151 16.7141 27.7526 17.5353C27.58 18.4079 26.7327 18.9753 25.8602 18.8026C24.9877 18.63 24.4203 17.7828 24.5929 16.9103C24.7149 16.2937 24.7791 15.6552 24.7791 15C24.7791 14.3448 24.7149 13.7063 24.5929 13.0897C24.4203 12.2172 24.9877 11.37 25.8602 11.1974ZM4.63292 19.99C5.372 19.4952 6.37227 19.6932 6.86708 20.4323C7.5815 21.4994 8.50059 22.4185 9.56771 23.1329C10.3068 23.6277 10.5048 24.628 10.01 25.3671C9.51521 26.1062 8.51495 26.3042 7.77586 25.8094C6.35889 24.8607 5.13925 23.6411 4.19061 22.2241C3.69581 21.4851 3.89383 20.4848 4.63292 19.99ZM25.3671 19.99C26.1062 20.4848 26.3042 21.4851 25.8094 22.2241C24.8607 23.6411 23.6411 24.8607 22.2241 25.8094C21.4851 26.3042 20.4848 26.1062 19.99 25.3671C19.4952 24.628 19.6932 23.6277 20.4323 23.1329C21.4994 22.4185 22.4185 21.4994 23.1329 20.4323C23.6277 19.6932 24.628 19.4952 25.3671 19.99ZM11.1974 25.8602C11.37 24.9877 12.2172 24.4203 13.0897 24.5929C13.7063 24.7149 14.3448 24.7791 15 24.7791C15.6552 24.7791 16.2937 24.7149 16.9103 24.5929C17.7828 24.4203 18.63 24.9877 18.8026 25.8602C18.9752 26.7327 18.4079 27.58 17.5353 27.7526C16.7141 27.9151 15.8661 28 15 28C14.1339 28 13.2859 27.9151 12.4647 27.7526C11.5921 27.58 11.0247 26.7327 11.1974 25.8602Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/dash-dotted.svg b/apps/web/public/icons/icon/dash-dotted.svg
new file mode 100644
index 00000000..2b01b4f9
--- /dev/null
+++ b/apps/web/public/icons/icon/dash-dotted.svg
@@ -0,0 +1,14 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M17 3.7915C17 4.89607 16.1046 5.7915 15 5.7915C13.8954 5.7915 13 4.89607 13 3.7915C13 2.68693 13.8954 1.7915 15 1.7915C16.1046 1.7915 17 2.68693 17 3.7915Z" fill="black"/>
+<path d="M17 25.7915C17 26.8961 16.1046 27.7915 15 27.7915C13.8954 27.7915 13 26.8961 13 25.7915C13 24.6869 13.8954 23.7915 15 23.7915C16.1046 23.7915 17 24.6869 17 25.7915Z" fill="black"/>
+<path d="M22.232 6.26525C21.6798 7.22184 20.4566 7.54959 19.5 6.9973C18.5434 6.44502 18.2157 5.22184 18.7679 4.26525C19.3202 3.30867 20.5434 2.98092 21.5 3.5332C22.4566 4.08549 22.7843 5.30867 22.232 6.26525Z" fill="black"/>
+<path d="M11.232 25.3178C10.6798 26.2744 9.45658 26.6021 8.5 26.0499C7.54341 25.4976 7.21566 24.2744 7.76794 23.3178C8.32023 22.3612 9.54341 22.0335 10.5 22.5858C11.4566 23.138 11.7843 24.3612 11.232 25.3178Z" fill="black"/>
+<path d="M25.5262 11.0236C24.5697 11.5758 23.3465 11.2481 22.7942 10.2915C22.2419 9.33491 22.5697 8.11173 23.5263 7.55945C24.4828 7.00716 25.706 7.33491 26.2583 8.2915C26.8106 9.24808 26.4828 10.4713 25.5262 11.0236Z" fill="black"/>
+<path d="M6.47376 22.0236C5.51717 22.5758 4.29399 22.2481 3.74171 21.2915C3.18942 20.3349 3.51717 19.1117 4.47376 18.5594C5.43035 18.0072 6.65353 18.3349 7.20581 19.2915C7.7581 20.2481 7.43035 21.4713 6.47376 22.0236Z" fill="black"/>
+<path d="M26 16.7915C24.8954 16.7915 24 15.8961 24 14.7915C24 13.6869 24.8954 12.7915 26 12.7915C27.1046 12.7915 28 13.6869 28 14.7915C28 15.8961 27.1046 16.7915 26 16.7915Z" fill="black"/>
+<path d="M4 16.7915C2.89543 16.7915 2 15.8961 2 14.7915C2 13.6869 2.89543 12.7915 4 12.7915C5.10457 12.7915 6 13.6869 6 14.7915C6 15.8961 5.10457 16.7915 4 16.7915Z" fill="black"/>
+<path d="M23.5262 22.0236C22.5697 21.4713 22.2419 20.2481 22.7942 19.2915C23.3465 18.3349 24.5697 18.0072 25.5263 18.5594C26.4828 19.1117 26.8106 20.3349 26.2583 21.2915C25.706 22.2481 24.4828 22.5758 23.5262 22.0236Z" fill="black"/>
+<path d="M4.47376 11.0236C3.51717 10.4713 3.18942 9.24808 3.74171 8.2915C4.29399 7.33491 5.51717 7.00716 6.47376 7.55945C7.43035 8.11173 7.7581 9.33491 7.20581 10.2915C6.65353 11.2481 5.43034 11.5758 4.47376 11.0236Z" fill="black"/>
+<path d="M18.768 25.3178C18.2157 24.3612 18.5434 23.138 19.5 22.5857C20.4566 22.0334 21.6798 22.3612 22.2321 23.3178C22.7843 24.2743 22.4566 25.4975 21.5 26.0498C20.5434 26.6021 19.3202 26.2743 18.768 25.3178Z" fill="black"/>
+<path d="M7.76795 6.2652C7.21567 5.30862 7.54342 4.08544 8.5 3.53315C9.45659 2.98087 10.6798 3.30862 11.2321 4.2652C11.7843 5.22179 11.4566 6.44497 10.5 6.99725C9.54342 7.54954 8.32024 7.22179 7.76795 6.2652Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/dash-draw.svg b/apps/web/public/icons/icon/dash-draw.svg
new file mode 100644
index 00000000..75ecb72b
--- /dev/null
+++ b/apps/web/public/icons/icon/dash-draw.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M21.888 15.7331C21.888 11.5738 17.8658 8.92277 13.1123 8.92277C9.48121 8.92277 6.98189 11.5642 6.27145 14.1692C4.96079 18.9749 7.8945 23.0277 11.6746 24.0908C17.7126 25.2814 21.888 21.4921 21.888 15.7331ZM17.1431 2.1844C22.0563 2.9037 26.0951 6.6011 27.4241 11.3531C31.2814 24.4598 14.8135 33.1322 6.27145 24.5902C-3.45849 14.8611 4.83063 0.187141 17.1431 2.1844Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/dash-solid.svg b/apps/web/public/icons/icon/dash-solid.svg
new file mode 100644
index 00000000..dbbe4aae
--- /dev/null
+++ b/apps/web/public/icons/icon/dash-solid.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="15" cy="15" r="11.5" stroke="black" stroke-width="3"/>
+</svg>
diff --git a/apps/web/public/icons/icon/disconnected.svg b/apps/web/public/icons/icon/disconnected.svg
new file mode 100644
index 00000000..335f5031
--- /dev/null
+++ b/apps/web/public/icons/icon/disconnected.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M14.6246 8.08966C8.94285 8.1985 3.90974 10.7717 0.702407 14.7107C0.294387 15.2118 0.414914 15.9436 0.934298 16.3281C1.47615 16.7292 2.23941 16.5967 2.66586 16.0746C5.14686 13.0369 8.88675 10.9431 13.1591 10.4712L14.6246 8.08966ZM10.7967 14.3104C8.56295 15.0878 6.64328 16.4668 5.26387 18.2363C4.90293 18.6993 5.02803 19.3584 5.4999 19.7077C6.03125 20.101 6.78272 19.9442 7.19471 19.4272C7.56521 18.9623 7.98227 18.5316 8.43945 18.1411L10.7967 14.3104ZM20.3687 17.2713L21.5219 15.3972C22.7718 16.1511 23.8624 17.1153 24.7362 18.2362C25.0972 18.6992 24.9721 19.3583 24.5002 19.7076C23.9689 20.1009 23.2174 19.9442 22.8054 19.4272C22.1335 18.584 21.3085 17.8534 20.3687 17.2713ZM17.4759 21.9723L18.5682 20.1972C19.2365 20.6457 19.8039 21.2169 20.2326 21.8754C20.4882 22.268 20.3516 22.7786 19.975 23.0573C19.4174 23.4701 18.5978 23.1841 18.1652 22.6416C17.9665 22.3924 17.7347 22.1674 17.4759 21.9723ZM23.2506 12.5879L24.4705 10.6054C26.3359 11.6891 27.9726 13.0833 29.2977 14.7106C29.7057 15.2117 29.5852 15.9436 29.0658 16.3281C28.5239 16.7291 27.7607 16.5967 27.3342 16.0746C26.2093 14.6972 24.8255 13.5139 23.2506 12.5879Z" fill="black"/>
+<rect x="23.376" y="0.169067" width="3" height="33.372" rx="1.5" transform="rotate(31.6059 23.376 0.169067)" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/discord.svg b/apps/web/public/icons/icon/discord.svg
new file mode 100644
index 00000000..46d0f2d9
--- /dev/null
+++ b/apps/web/public/icons/icon/discord.svg
@@ -0,0 +1,12 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<g clip-path="url(#clip0_916_107895)">
+<path d="M8.16097 14.0244C8.64823 13.4997 9.30953 13.2032 10.0001 13.2C10.6908 13.2032 11.3521 13.4997 11.8393 14.0244C12.3266 14.5492 12.6001 15.2595 12.6001 16C12.6001 16.7404 12.3266 17.4507 11.8393 17.9755C11.3521 18.5002 10.6908 18.7967 10.0001 18.8C9.30953 18.7967 8.64823 18.5002 8.16097 17.9755C7.67371 17.4507 7.40015 16.7404 7.40015 16C7.40015 15.2595 7.67371 14.5492 8.16097 14.0244Z" fill="black"/>
+<path d="M18.1585 14.0244C18.6458 13.4997 19.3071 13.2032 19.9977 13.2C20.6883 13.2032 21.3496 13.4997 21.8369 14.0244C22.3241 14.5492 22.5977 15.2595 22.5977 16C22.5977 16.7404 22.3241 17.4507 21.8369 17.9755C21.3496 18.5002 20.6883 18.7967 19.9977 18.8C19.3071 18.7967 18.6458 18.5002 18.1585 17.9755C17.6713 17.4507 17.3977 16.7404 17.3977 16C17.3977 15.2595 17.6713 14.5492 18.1585 14.0244Z" fill="black"/>
+<path d="M12 6.79999C11.7912 6.08523 10.7517 4.71816 10 4.60004C8.47088 4.76658 6.03082 5.624 4.6 6.40004C3.57476 7.57227 2.52044 10.5558 2.10918 12.2C1.425 14.9355 1.04398 18.2476 0.994873 21.4C1.95111 23.2185 5.42945 25.2 7.5 25.4C7.98332 24.876 9.4 22.6 9.4 22.6M17.9951 6.79999C18.2039 6.08523 19.2434 4.71816 19.9951 4.60004C21.5242 4.76658 23.9643 5.624 25.3951 6.40004C26.4204 7.57227 27.4747 10.5558 27.8859 12.2C28.5701 14.9355 28.9511 18.2476 29.0002 21.4C28.044 23.2185 24.5657 25.2 22.4951 25.4C22.0118 24.876 20.5951 22.6 20.5951 22.6M7 9C7.6 8.2 9.99998 7 15 7C20 7 22.4 8.2 23 9M5.80005 20.4002C6.6 21.6002 8.40002 23.0002 15 23.0002C21.6001 23.0002 23.4 21.6003 24.2001 20.4002" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</g>
+<defs>
+<clipPath id="clip0_916_107895">
+<rect width="30" height="30" fill="white"/>
+</clipPath>
+</defs>
+</svg>
diff --git a/apps/web/public/icons/icon/distribute-horizontal.svg b/apps/web/public/icons/icon/distribute-horizontal.svg
new file mode 100644
index 00000000..e2ba5dbc
--- /dev/null
+++ b/apps/web/public/icons/icon/distribute-horizontal.svg
@@ -0,0 +1,6 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M1.99976 12H10.9998C11.552 12 11.9998 12.4477 11.9998 13V17C11.9998 17.5523 11.552 18 10.9998 18H1.99976V12Z" fill="black"/>
+<path d="M17.9998 13C17.9998 12.4477 18.4475 12 18.9998 12H27.9998V18H18.9998C18.4475 18 17.9998 17.5523 17.9998 17V13Z" fill="black"/>
+<path d="M0 3C0 2.44772 0.447715 2 1 2C1.55228 2 2 2.44772 2 3V27C2 27.5523 1.55228 28 1 28C0.447715 28 0 27.5523 0 27V3Z" fill="black"/>
+<path d="M27.9998 3C27.9998 2.44772 28.4475 2 28.9998 2C29.552 2 29.9998 2.44772 29.9998 3V27C29.9998 27.5523 29.552 28 28.9998 28C28.4475 28 27.9998 27.5523 27.9998 27V3Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/distribute-vertical.svg b/apps/web/public/icons/icon/distribute-vertical.svg
new file mode 100644
index 00000000..ebc9ad4a
--- /dev/null
+++ b/apps/web/public/icons/icon/distribute-vertical.svg
@@ -0,0 +1,6 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M12.0002 2L18.0002 2V11C18.0002 11.5523 17.5525 12 17.0002 12H13.0002C12.448 12 12.0002 11.5523 12.0002 11V2Z" fill="black"/>
+<path d="M12.0002 19C12.0002 18.4477 12.448 18 13.0002 18H17.0002C17.5525 18 18.0002 18.4477 18.0002 19V28H12.0002V19Z" fill="black"/>
+<path d="M2 1C2 0.447715 2.44772 0 3 0H27C27.5523 0 28 0.447715 28 1C28 1.55228 27.5523 2 27 2H3C2.44772 2 2 1.55228 2 1Z" fill="black"/>
+<path d="M2 29C2 28.4477 2.44772 28 3 28H27C27.5523 28 28 28.4477 28 29C28 29.5523 27.5523 30 27 30H3C2.44772 30 2 29.5523 2 29Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/dot.svg b/apps/web/public/icons/icon/dot.svg
new file mode 100644
index 00000000..aaedc6a1
--- /dev/null
+++ b/apps/web/public/icons/icon/dot.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="14.5" cy="15.5" r="4.5" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/dots-horizontal.svg b/apps/web/public/icons/icon/dots-horizontal.svg
new file mode 100644
index 00000000..ea5ba70f
--- /dev/null
+++ b/apps/web/public/icons/icon/dots-horizontal.svg
@@ -0,0 +1,5 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M7.25 15C7.25 16.2426 6.24264 17.25 5 17.25C3.75736 17.25 2.75 16.2426 2.75 15C2.75 13.7574 3.75736 12.75 5 12.75C6.24264 12.75 7.25 13.7574 7.25 15Z" fill="black"/>
+<path d="M17.25 15C17.25 16.2426 16.2426 17.25 15 17.25C13.7574 17.25 12.75 16.2426 12.75 15C12.75 13.7574 13.7574 12.75 15 12.75C16.2426 12.75 17.25 13.7574 17.25 15Z" fill="black"/>
+<path d="M27.25 15C27.25 16.2426 26.2426 17.25 25 17.25C23.7574 17.25 22.75 16.2426 22.75 15C22.75 13.7574 23.7574 12.75 25 12.75C26.2426 12.75 27.25 13.7574 27.25 15Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/dots-vertical.svg b/apps/web/public/icons/icon/dots-vertical.svg
new file mode 100644
index 00000000..d52e38a8
--- /dev/null
+++ b/apps/web/public/icons/icon/dots-vertical.svg
@@ -0,0 +1,5 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M17.25 5C17.25 6.24264 16.2426 7.25 15 7.25C13.7574 7.25 12.75 6.24264 12.75 5C12.75 3.75736 13.7574 2.75 15 2.75C16.2426 2.75 17.25 3.75736 17.25 5Z" fill="black"/>
+<path d="M17.25 15C17.25 16.2426 16.2426 17.25 15 17.25C13.7574 17.25 12.75 16.2426 12.75 15C12.75 13.7574 13.7574 12.75 15 12.75C16.2426 12.75 17.25 13.7574 17.25 15Z" fill="black"/>
+<path d="M17.25 25C17.25 26.2426 16.2426 27.25 15 27.25C13.7574 27.25 12.75 26.2426 12.75 25C12.75 23.7574 13.7574 22.75 15 22.75C16.2426 22.75 17.25 23.7574 17.25 25Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/drag-handle-dots.svg b/apps/web/public/icons/icon/drag-handle-dots.svg
new file mode 100644
index 00000000..836cb0c2
--- /dev/null
+++ b/apps/web/public/icons/icon/drag-handle-dots.svg
@@ -0,0 +1,8 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M11 9.25C12.2426 9.25 13.25 8.24264 13.25 7C13.25 5.75736 12.2426 4.75 11 4.75C9.75736 4.75 8.75 5.75736 8.75 7C8.75 8.24264 9.75736 9.25 11 9.25Z" fill="black"/>
+<path d="M19 9.25C20.2426 9.25 21.25 8.24264 21.25 7C21.25 5.75736 20.2426 4.75 19 4.75C17.7574 4.75 16.75 5.75736 16.75 7C16.75 8.24264 17.7574 9.25 19 9.25Z" fill="black"/>
+<path d="M21.25 15C21.25 16.2426 20.2426 17.25 19 17.25C17.7574 17.25 16.75 16.2426 16.75 15C16.75 13.7574 17.7574 12.75 19 12.75C20.2426 12.75 21.25 13.7574 21.25 15Z" fill="black"/>
+<path d="M11 17.25C12.2426 17.25 13.25 16.2426 13.25 15C13.25 13.7574 12.2426 12.75 11 12.75C9.75736 12.75 8.75 13.7574 8.75 15C8.75 16.2426 9.75736 17.25 11 17.25Z" fill="black"/>
+<path d="M21.25 23C21.25 24.2426 20.2426 25.25 19 25.25C17.7574 25.25 16.75 24.2426 16.75 23C16.75 21.7574 17.7574 20.75 19 20.75C20.2426 20.75 21.25 21.7574 21.25 23Z" fill="black"/>
+<path d="M11 25.25C12.2426 25.25 13.25 24.2426 13.25 23C13.25 21.7574 12.2426 20.75 11 20.75C9.75736 20.75 8.75 21.7574 8.75 23C8.75 24.2426 9.75736 25.25 11 25.25Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/duplicate.svg b/apps/web/public/icons/icon/duplicate.svg
new file mode 100644
index 00000000..3e28e611
--- /dev/null
+++ b/apps/web/public/icons/icon/duplicate.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M2 19.0001C2 20.6569 3.34315 22.0001 5 22.0001H8L8 20.0001H5C4.44772 20.0001 4 19.5524 4 19.0001L4 5.00009C4 4.44781 4.44772 4.00009 5 4.00009L19 4.00009C19.5523 4.00009 20 4.44781 20 5.00009V8.00001H11C9.34315 8.00001 8 9.34316 8 11V25C8 26.6569 9.34315 28 11 28H25C26.6569 28 28 26.6569 28 25V11C28 9.34316 26.6569 8.00001 25 8.00001H22V5.00009C22 3.34324 20.6569 2.00009 19 2.00009H5C3.34315 2.00009 2 3.34324 2 5.00009V19.0001ZM10 11C10 10.4477 10.4477 10 11 10H25C25.5523 10 26 10.4477 26 11V25C26 25.5523 25.5523 26 25 26H11C10.4477 26 10 25.5523 10 25V11Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/edit.svg b/apps/web/public/icons/icon/edit.svg
new file mode 100644
index 00000000..58b9fb63
--- /dev/null
+++ b/apps/web/public/icons/icon/edit.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M24 9.27273L11.7954 21.4774C11.7504 21.5223 11.6972 21.5583 11.6388 21.5834L6 24L8.41661 18.3612C8.44167 18.3028 8.47765 18.2496 8.52263 18.2046L20.7273 6L24 9.27273Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/external-link.svg b/apps/web/public/icons/icon/external-link.svg
new file mode 100644
index 00000000..0ffe9397
--- /dev/null
+++ b/apps/web/public/icons/icon/external-link.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M13 5H7C5.89543 5 5 5.89543 5 7V23C5 24.1046 5.89543 25 7 25H23C24.1046 25 25 24.1046 25 23V17M19 5H25M25 5V11M25 5L13 17" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/fill-none.svg b/apps/web/public/icons/icon/fill-none.svg
new file mode 100644
index 00000000..858806ce
--- /dev/null
+++ b/apps/web/public/icons/icon/fill-none.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M8 4H26V22H24V7.99951C24 6.89494 23.1046 5.99951 22 5.99951H8V4ZM6 5.99951V4C6 2.89543 6.89543 2 8 2H26C27.1046 2 28 2.89543 28 4V22C28 23.1046 27.1046 24 26 24H24V25.9995C24 27.1041 23.1046 27.9995 22 27.9995H4C2.89543 27.9995 2 27.1041 2 25.9995V7.99951C2 6.89494 2.89543 5.99951 4 5.99951H6ZM22 24V25.9995H4L4 7.99951H6V22C6 23.1046 6.89543 24 8 24H22ZM22 22H8L8 7.99951H22V22Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/fill-pattern.svg b/apps/web/public/icons/icon/fill-pattern.svg
new file mode 100644
index 00000000..200390db
--- /dev/null
+++ b/apps/web/public/icons/icon/fill-pattern.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M26 4H8V5.99951H22C23.1046 5.99951 24 6.89494 24 7.99951V22H26V4ZM6 4V5.99951H4C2.89543 5.99951 2 6.89494 2 7.99951V25.9995C2 27.1041 2.89543 27.9995 4 27.9995H22C23.1046 27.9995 24 27.1041 24 25.9995V24H26C27.1046 24 28 23.1046 28 22V4C28 2.89543 27.1046 2 26 2H8C6.89543 2 6 2.89543 6 4ZM22 25.9995L4 26V8L22 7.99951V25.9995Z" fill="black"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M23.4375 15.5525C23.8488 15.9439 23.8488 16.5786 23.4375 16.97L21.9482 18.3875C21.5369 18.7789 20.8701 18.7789 20.4589 18.3875C20.0476 17.996 20.0476 17.3614 20.4589 16.97L21.9482 15.5525C22.3595 15.1611 23.0263 15.1611 23.4375 15.5525ZM18.9695 21.2224C19.3808 20.831 19.3808 20.1964 18.9695 19.8049C18.5583 19.4135 17.8915 19.4135 17.4802 19.8049L15.9909 21.2224C15.5796 21.6138 15.5796 22.2485 15.9909 22.6399C16.4021 23.0313 17.0689 23.0313 17.4802 22.6399L18.9695 21.2224ZM14.5015 24.0574C14.9128 24.4488 14.9128 25.0834 14.5015 25.4748L13.0122 26.8923C12.6009 27.2837 11.9342 27.2837 11.5229 26.8923C11.1116 26.5009 11.1116 25.8663 11.5229 25.4748L13.0122 24.0574C13.4235 23.6659 14.0903 23.6659 14.5015 24.0574ZM14.5015 7.0476C14.9128 7.43903 14.9128 8.07365 14.5015 8.46507L13.0122 9.88256C12.6009 10.274 11.9341 10.274 11.5229 9.88256C11.1116 9.49113 11.1116 8.8565 11.5229 8.46507L13.0122 7.0476C13.4235 6.65618 14.0903 6.65618 14.5015 7.0476ZM10.0335 12.7175C10.4448 12.3261 10.4448 11.6915 10.0335 11.3C9.62228 10.9086 8.95548 10.9086 8.54421 11.3L7.05488 12.7175C6.64361 13.1089 6.64361 13.7436 7.05488 14.135C7.46614 14.5264 8.13294 14.5264 8.54421 14.135L10.0335 12.7175ZM5.56555 15.5525C5.97682 15.9439 5.97682 16.5785 5.56555 16.9699L4.07622 18.3874C3.66495 18.7788 2.99816 18.7788 2.58689 18.3874C2.17563 17.996 2.17563 17.3614 2.58689 16.9699L4.07622 15.5525C4.48749 15.161 5.15429 15.161 5.56555 15.5525ZM23.4375 11.2998C23.8488 10.9083 23.8488 10.2737 23.4375 9.88229C23.0263 9.49086 22.3595 9.49086 21.9482 9.88229L20.4589 11.2998C20.0476 11.6912 20.0476 12.3258 20.4589 12.7172C20.8702 13.1087 21.5369 13.1087 21.9482 12.7172L23.4375 11.2998ZM18.9696 14.1347C19.3808 14.5261 19.3808 15.1608 18.9696 15.5522L17.4802 16.9697C17.069 17.3611 16.4022 17.3611 15.9909 16.9697C15.5796 16.5782 15.5796 15.9436 15.9909 15.5522L17.4802 14.1347C17.8915 13.7433 18.5583 13.7433 18.9696 14.1347ZM14.5016 19.8046C14.9128 19.4132 14.9128 18.7786 14.5016 18.3871C14.0903 17.9957 13.4235 17.9957 13.0122 18.3871L11.5229 19.8046C11.1116 20.196 11.1116 20.8307 11.5229 21.2221C11.9342 21.6135 12.601 21.6135 13.0122 21.2221L14.5016 19.8046ZM10.0336 22.6396C10.4448 23.031 10.4448 23.6656 10.0336 24.057L8.54425 25.4745C8.13298 25.8659 7.46619 25.8659 7.05492 25.4745C6.64365 25.0831 6.64365 24.4485 7.05492 24.057L8.54425 22.6396C8.95551 22.2481 9.62231 22.2481 10.0336 22.6396ZM10.0336 7.04727L8.54424 8.46474C8.13297 8.85617 7.46618 8.85617 7.05491 8.46474C6.64364 8.07332 6.64364 7.43869 7.05491 7.04727C8.15861 5.99681 11.8257 5.34161 10.0336 7.04727ZM5.56558 11.2997C5.97685 10.9083 5.97685 10.2736 5.56558 9.88221C5.15431 9.49079 4.48751 9.49079 4.07625 9.88222L2.58692 11.2997C2.17565 11.6911 2.17565 12.3257 2.58692 12.7172C2.99818 13.1086 3.66498 13.1086 4.07625 12.7172L5.56558 11.2997ZM23.4375 21.2223C23.8488 21.6137 23.8488 22.2484 23.4375 22.6398L21.9482 24.0573C21.5369 24.4487 20.8701 24.4487 20.4589 24.0573C20.0476 23.6658 20.0476 23.0312 20.4589 22.6398L21.9482 21.2223C22.3595 20.8309 23.0263 20.8309 23.4375 21.2223ZM18.9695 26.8922C19.3808 26.5008 19.3808 25.8662 18.9695 25.4747C18.5583 25.0833 17.8915 25.0833 17.4802 25.4747L15.9909 26.8922C14.172 28.6233 17.8497 27.958 18.9695 26.8922ZM18.9695 9.88244C19.3808 9.49101 19.3808 8.85639 18.9695 8.46496C18.5583 8.07354 17.8915 8.07354 17.4802 8.46496L15.9909 9.88244C15.5796 10.2739 15.5796 10.9085 15.9909 11.2999C16.4021 11.6913 17.0689 11.6913 17.4802 11.2999L18.9695 9.88244ZM14.5015 12.7174C14.9128 13.1088 14.9128 13.7434 14.5015 14.1349L13.0122 15.5523C12.6009 15.9438 11.9341 15.9438 11.5229 15.5523C11.1116 15.1609 11.1116 14.5263 11.5229 14.1349L13.0122 12.7174C13.4235 12.326 14.0903 12.326 14.5015 12.7174ZM10.0335 18.3873C10.4448 17.9959 10.4448 17.3612 10.0335 16.9698C9.62228 16.5784 8.95548 16.5784 8.54421 16.9698L7.05488 18.3873C6.64361 18.7787 6.64361 19.4133 7.05488 19.8048C7.46614 20.1962 8.13294 20.1962 8.54421 19.8048L10.0335 18.3873ZM5.56555 21.2222C5.97682 21.6137 5.97682 22.2483 5.56555 22.6397L4.07622 24.0572C3.66495 24.4486 2.99816 24.4486 2.58689 24.0572C2.17563 23.6658 2.17563 23.0311 2.58689 22.6397L4.07622 21.2222C4.48749 20.8308 5.15429 20.8308 5.56555 21.2222Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/fill-semi.svg b/apps/web/public/icons/icon/fill-semi.svg
new file mode 100644
index 00000000..2894622a
--- /dev/null
+++ b/apps/web/public/icons/icon/fill-semi.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M26 4H8V5.99951H22C23.1046 5.99951 24 6.89494 24 7.99951V22H26V4ZM6 4V5.99951H4C2.89543 5.99951 2 6.89494 2 7.99951V25.9995C2 27.1041 2.89543 27.9995 4 27.9995H22C23.1046 27.9995 24 27.1041 24 25.9995V24H26C27.1046 24 28 23.1046 28 22V4C28 2.89543 27.1046 2 26 2H8C6.89543 2 6 2.89543 6 4ZM22 25.9995V26H4L4 25.9995L4 8V7.99951H6V8H8V7.99951H22V8V22V24V25.9995Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/fill-solid.svg b/apps/web/public/icons/icon/fill-solid.svg
new file mode 100644
index 00000000..b2054f09
--- /dev/null
+++ b/apps/web/public/icons/icon/fill-solid.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M26 4H8V5.99951H22C23.1046 5.99951 24 6.89494 24 7.99951V22H26V4ZM6 4V5.99951H4C2.89543 5.99951 2 6.89494 2 7.99951V25.9995C2 27.1041 2.89543 27.9995 4 27.9995H22C23.1046 27.9995 24 27.1041 24 25.9995V24H26C27.1046 24 28 23.1046 28 22V4C28 2.89543 27.1046 2 26 2H8C6.89543 2 6 2.89543 6 4ZM22 25.9995L4 26V8L22 7.99951V25.9995Z" fill="black"/>
+<path d="M4 8L22 7.99951V25.9995L4 26V8Z" fill="black" fill-opacity="0.32"/>
+</svg>
diff --git a/apps/web/public/icons/icon/follow.svg b/apps/web/public/icons/icon/follow.svg
new file mode 100644
index 00000000..fa525c35
--- /dev/null
+++ b/apps/web/public/icons/icon/follow.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M19 15C19 17.2091 17.2091 19 15 19C12.7909 19 11 17.2091 11 15C11 12.7909 12.7909 11 15 11C17.2091 11 19 12.7909 19 15Z" fill="black"/>
+<path d="M26 15C26 16.7708 24.9234 18.4963 22.9306 19.8248C20.9448 21.1487 18.1438 22 15 22C11.8562 22 9.05522 21.1487 7.06942 19.8248C5.07663 18.4963 4 16.7708 4 15C4 13.2292 5.07663 11.5037 7.06942 10.1752C9.05522 8.85133 11.8562 8 15 8C18.1438 8 20.9448 8.85133 22.9306 10.1752C24.9234 11.5037 26 13.2292 26 15Z" stroke="black" stroke-width="2"/>
+</svg>
diff --git a/apps/web/public/icons/icon/following.svg b/apps/web/public/icons/icon/following.svg
new file mode 100644
index 00000000..918caa50
--- /dev/null
+++ b/apps/web/public/icons/icon/following.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M26 15C26 16.7708 24.9234 18.4963 22.9306 19.8248C20.9448 21.1487 18.1438 22 15 22C11.8562 22 9.05522 21.1487 7.06942 19.8248C5.07663 18.4963 4 16.7708 4 15C4 13.2292 5.07663 11.5037 7.06942 10.1752C9.05522 8.85133 11.8562 8 15 8C18.1438 8 20.9448 8.85133 22.9306 10.1752C24.9234 11.5037 26 13.2292 26 15Z" stroke="black" stroke-width="2"/>
+<path d="M12 15C12 17.2091 10.433 19 8.5 19C6.567 19 5 17.2091 5 15C5 12.7909 6.567 11 8.5 11C10.433 11 12 12.7909 12 15Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/font-draw.svg b/apps/web/public/icons/icon/font-draw.svg
new file mode 100644
index 00000000..3bcb5509
--- /dev/null
+++ b/apps/web/public/icons/icon/font-draw.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M22.9653 11.963C19.0295 11.963 15.66 18.3319 19.0568 21.626C22.4536 24.92 24.844 17.453 24.9101 14.5982C24.9762 11.7433 26.3161 19.7261 28.2027 22.0685M1.80066 18.5791C1.80066 18.5791 6.13657 17.2677 13.0913 17.1801M3.72369 10.8073C3.63751 13.382 3.63214 15.4219 3.63908 17.0238M3.63908 17.0238C3.65333 20.3146 3.71952 21.7569 3.24375 22.1894C2.94958 22.4569 3.08476 19.9997 3.63908 17.0238ZM3.63908 17.0238C4.41765 12.8439 6.02309 7.64045 8.42708 7.52541C12.5426 7.32846 9.81291 15.6345 14.25 22.5798" stroke="black" stroke-width="3.2" stroke-linecap="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/font-mono.svg b/apps/web/public/icons/icon/font-mono.svg
new file mode 100644
index 00000000..028596a9
--- /dev/null
+++ b/apps/web/public/icons/icon/font-mono.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M11.358 23.9353L10.0815 19.3244H4.68909L3.43867 23.9353H0L5.1059 5.75208H9.84709L14.979 23.9353H11.358ZM7.50254 9.11259H7.29414L5.28825 16.537H9.50843L7.50254 9.11259Z" fill="black"/>
+<path d="M28.0462 23.9353C27.23 23.9353 26.5961 23.7356 26.1445 23.3362C25.7104 22.9194 25.4498 22.3462 25.363 21.6168H25.2328C24.9896 22.4504 24.512 23.1017 23.8 23.5706C23.0879 24.0222 22.2109 24.2479 21.1689 24.2479C19.8143 24.2479 18.7375 23.8919 17.9386 23.1799C17.1397 22.4678 16.7403 21.4779 16.7403 20.2101C16.7403 17.5008 18.7288 16.1462 22.7059 16.1462H25.0765V15.2605C25.0765 14.4095 24.868 13.7669 24.4512 13.3328C24.0344 12.8986 23.3571 12.6815 22.4193 12.6815C21.5683 12.6815 20.8823 12.8465 20.3613 13.1765C19.8403 13.5064 19.3974 13.9319 19.0327 14.4529L17.1311 12.8378C17.5479 12.091 18.2165 11.4658 19.1369 10.9622C20.0748 10.4412 21.2818 10.1807 22.758 10.1807C24.5294 10.1807 25.9101 10.5975 26.9 11.4311C27.9073 12.2473 28.4109 13.4717 28.4109 15.1042V21.4345H30V23.9353H28.0462ZM22.2891 21.9555C23.0879 21.9555 23.7479 21.7731 24.2689 21.4084C24.8073 21.0264 25.0765 20.514 25.0765 19.8714V18.074H22.784C20.9605 18.074 20.0487 18.6471 20.0487 19.7933V20.3143C20.0487 20.8527 20.2484 21.2608 20.6479 21.5387C21.0473 21.8165 21.5944 21.9555 22.2891 21.9555Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/font-sans.svg b/apps/web/public/icons/icon/font-sans.svg
new file mode 100644
index 00000000..2a0c28f0
--- /dev/null
+++ b/apps/web/public/icons/icon/font-sans.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M12.5042 23.9998L11.0838 19.4851H4.79369L3.3987 23.9998H0L5.93505 6.29614H10.0947L15.979 23.9998H12.5042ZM7.98948 9.33976H7.86267L5.60532 16.6191H10.2722L7.98948 9.33976Z" fill="black"/>
+<path d="M28.1992 23.9998C27.489 23.9998 26.9226 23.7969 26.4998 23.3911C26.094 22.9684 25.8404 22.4104 25.7389 21.7171H25.5868C25.3669 22.5795 24.9189 23.2305 24.2425 23.6701C23.5661 24.0928 22.7291 24.3042 21.7315 24.3042C20.3788 24.3042 19.3389 23.9491 18.6118 23.2389C17.8847 22.5287 17.5212 21.5818 17.5212 20.3982C17.5212 19.0286 18.0115 18.0141 18.9923 17.3546C19.973 16.6782 21.368 16.3401 23.1772 16.3401H25.4346V15.3763C25.4346 14.6323 25.2401 14.0574 24.8512 13.6515C24.4623 13.2457 23.8367 13.0428 22.9743 13.0428C22.2134 13.0428 21.5962 13.2119 21.1228 13.5501C20.6663 13.8714 20.2773 14.2603 19.9561 14.7168L18.0285 12.9921C18.5188 12.2312 19.1698 11.6225 19.9814 11.1659C20.7931 10.6925 21.8668 10.4557 23.2026 10.4557C24.9949 10.4557 26.3561 10.8616 27.2861 11.6732C28.2161 12.4848 28.6811 13.6515 28.6811 15.1733V21.4128H30V23.9998H28.1992ZM22.8221 21.9454C23.5492 21.9454 24.1664 21.7848 24.6737 21.4635C25.1809 21.1422 25.4346 20.6688 25.4346 20.0431V18.293H23.3548C21.6639 18.293 20.8184 18.8341 20.8184 19.9163V20.3475C20.8184 20.8886 20.9875 21.2944 21.3257 21.5649C21.6808 21.8186 22.1796 21.9454 22.8221 21.9454Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/font-serif.svg b/apps/web/public/icons/icon/font-serif.svg
new file mode 100644
index 00000000..d3baaceb
--- /dev/null
+++ b/apps/web/public/icons/icon/font-serif.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M0 22.0031H1.26205L6.70619 6.21509H9.79945L15.2436 22.0031H16.5056V23.4879H9.65098V22.0031H11.7049L10.4181 18.118H4.4543L3.1675 22.0031H5.22143V23.4879H0V22.0031ZM4.89973 16.559H9.97268L7.49807 8.88766H7.37434L4.89973 16.559Z" fill="black"/>
+<path d="M21.6853 23.7848C20.349 23.7848 19.3592 23.4631 18.7158 22.8197C18.0724 22.1763 17.7507 21.2855 17.7507 20.1471C17.7507 18.8933 18.2044 17.9447 19.1117 17.3013C20.0356 16.6579 21.4708 16.3362 23.4175 16.3362H25.1745V14.7772C25.1745 13.7709 24.9683 13.012 24.5559 12.5006C24.1434 11.9727 23.467 11.7087 22.5267 11.7087C21.8008 11.7087 21.2399 11.816 20.8439 12.0304V12.1294C21.0089 12.2284 21.1656 12.3851 21.3141 12.5996C21.4791 12.7975 21.5616 13.078 21.5616 13.4409C21.5616 13.9194 21.4131 14.3153 21.1162 14.6288C20.8357 14.9257 20.415 15.0742 19.8541 15.0742C19.3757 15.0742 18.9797 14.9175 18.6663 14.604C18.3693 14.2906 18.2209 13.8781 18.2209 13.3667C18.2209 12.9708 18.3281 12.5913 18.5426 12.2284C18.7735 11.8489 19.1117 11.519 19.5571 11.2385C20.0026 10.9416 20.547 10.7106 21.1904 10.5457C21.8503 10.3642 22.6092 10.2734 23.467 10.2734C25.1168 10.2734 26.3541 10.6529 27.1789 11.4118C28.0038 12.1542 28.4163 13.2017 28.4163 14.5545V22.0278H30V23.2651C29.7525 23.4301 29.4226 23.5539 29.0102 23.6363C28.5977 23.7353 28.177 23.7848 27.7481 23.7848C26.9232 23.7848 26.3293 23.5868 25.9664 23.1909C25.6034 22.7785 25.422 22.2588 25.422 21.6319V21.5577H25.2982C25.1828 21.8381 25.0343 22.1103 24.8528 22.3743C24.6713 22.6382 24.4404 22.8775 24.1599 23.0919C23.8795 23.2899 23.533 23.4549 23.1206 23.5868C22.7246 23.7188 22.2462 23.7848 21.6853 23.7848ZM22.9474 21.8051C23.6073 21.8051 24.1434 21.6484 24.5559 21.335C24.9683 21.0215 25.1745 20.4936 25.1745 19.7512V17.6725H23.764C22.7741 17.6725 22.0648 17.854 21.6358 18.2169C21.2234 18.5634 21.0172 19.0666 21.0172 19.7265V20.1719C21.0172 20.7328 21.1904 21.1452 21.5368 21.4092C21.8998 21.6731 22.37 21.8051 22.9474 21.8051Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/geo-arrow-down.svg b/apps/web/public/icons/icon/geo-arrow-down.svg
new file mode 100644
index 00000000..c8de46ee
--- /dev/null
+++ b/apps/web/public/icons/icon/geo-arrow-down.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M15 26.8225L26.6544 12.6579C27.1913 12.0054 26.7271 11.0225 25.8822 11.0225H21.8V2.82251H8.2L8.2 11.0225H4.11777C3.27285 11.0225 2.80873 12.0054 3.34555 12.6579L15 26.8225Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/geo-arrow-left.svg b/apps/web/public/icons/icon/geo-arrow-left.svg
new file mode 100644
index 00000000..57e23fd1
--- /dev/null
+++ b/apps/web/public/icons/icon/geo-arrow-left.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M3 14.8225L17.1646 26.477C17.8171 27.0138 18.8 26.5497 18.8 25.7047L18.8 21.6225L27 21.6225L27 8.02251L18.8 8.02251L18.8 3.94028C18.8 3.09537 17.8171 2.63124 17.1646 3.16806L3 14.8225Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/geo-arrow-right.svg b/apps/web/public/icons/icon/geo-arrow-right.svg
new file mode 100644
index 00000000..ee6f147e
--- /dev/null
+++ b/apps/web/public/icons/icon/geo-arrow-right.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M27 14.8225L12.8354 3.16806C12.1829 2.63123 11.2 3.09536 11.2 3.94028L11.2 8.02251L3 8.02251L3 21.6225L11.2 21.6225L11.2 25.7047C11.2 26.5497 12.1829 27.0138 12.8354 26.477L27 14.8225Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/geo-arrow-up.svg b/apps/web/public/icons/icon/geo-arrow-up.svg
new file mode 100644
index 00000000..1b4c0be3
--- /dev/null
+++ b/apps/web/public/icons/icon/geo-arrow-up.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M15 2.82251L3.34555 16.9871C2.80872 17.6396 3.27285 18.6225 4.11776 18.6225L8.2 18.6225L8.19999 26.8225L21.8 26.8225L21.8 18.6225L25.8822 18.6225C26.7271 18.6225 27.1913 17.6396 26.6544 16.9871L15 2.82251Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/geo-check-box.svg b/apps/web/public/icons/icon/geo-check-box.svg
new file mode 100644
index 00000000..bc3572ee
--- /dev/null
+++ b/apps/web/public/icons/icon/geo-check-box.svg
@@ -0,0 +1,5 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M25 3C26.1046 3 27 3.89543 27 5V25C27 26.1046 26.1046 27 25 27H5C3.89543 27 3 26.1046 3 25V5C3 3.89543 3.89543 3 5 3H25Z" stroke="black" stroke-width="2"/>
+<path d="M8 15L13 22" stroke="black" stroke-width="2" stroke-linecap="round"/>
+<path d="M22 8L13 22" stroke="black" stroke-width="2" stroke-linecap="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/geo-cloud.svg b/apps/web/public/icons/icon/geo-cloud.svg
new file mode 100644
index 00000000..de6c0537
--- /dev/null
+++ b/apps/web/public/icons/icon/geo-cloud.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M22.3235 12.395L22.3443 13.258L23.201 13.3634C26.0952 13.7196 28.3367 16.1884 28.3367 19.1792C28.3367 22.4156 25.713 25.0393 22.4766 25.0393H7.52351C4.28709 25.0393 1.66345 22.4156 1.66345 19.1792C1.66345 16.1885 3.9048 13.7197 6.79891 13.3634L7.65558 13.258L7.67644 12.3951C7.7722 8.43304 11.0145 5.25024 15 5.25024C18.9854 5.25024 22.2277 8.43302 22.3235 12.395Z" stroke="black" stroke-width="2"/>
+</svg>
diff --git a/apps/web/public/icons/icon/geo-diamond.svg b/apps/web/public/icons/icon/geo-diamond.svg
new file mode 100644
index 00000000..72174f74
--- /dev/null
+++ b/apps/web/public/icons/icon/geo-diamond.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M16.4142 1.97064L28.0294 13.5859C28.8105 14.3669 28.8105 15.6332 28.0294 16.4143L16.4142 28.0295C15.6332 28.8106 14.3668 28.8106 13.5858 28.0295L1.97056 16.4143C1.18951 15.6332 1.18951 14.3669 1.97056 13.5859L13.5858 1.97064C14.3668 1.18959 15.6332 1.18959 16.4142 1.97064Z" stroke="black" stroke-width="2"/>
+</svg>
diff --git a/apps/web/public/icons/icon/geo-ellipse.svg b/apps/web/public/icons/icon/geo-ellipse.svg
new file mode 100644
index 00000000..b6a84881
--- /dev/null
+++ b/apps/web/public/icons/icon/geo-ellipse.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M27 15C27 21.6274 21.6274 27 15 27C8.3726 27 3 21.6274 3 15C3 8.37259 8.37259 3 15 3C21.6274 3 27 8.3726 27 15Z" stroke="black" stroke-width="2"/>
+</svg>
diff --git a/apps/web/public/icons/icon/geo-heart.svg b/apps/web/public/icons/icon/geo-heart.svg
new file mode 100644
index 00000000..1a015ffd
--- /dev/null
+++ b/apps/web/public/icons/icon/geo-heart.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M2 10.1181C2 0.953581 14.025 0.953581 15 8.30928C15.975 0.953581 28 0.953581 28 10.1181C28 17.9561 18.25 20.9707 15 27C11.75 20.9707 2 17.9561 2 10.1181Z" stroke="#1D1D1D" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/geo-hexagon.svg b/apps/web/public/icons/icon/geo-hexagon.svg
new file mode 100644
index 00000000..ec12957d
--- /dev/null
+++ b/apps/web/public/icons/icon/geo-hexagon.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M14.0086 3.21735C14.623 2.86663 15.377 2.86663 15.9915 3.21735L24.8171 8.25497C25.4407 8.61092 25.8256 9.27387 25.8256 9.99193V20.0081C25.8256 20.7261 25.4407 21.3891 24.8171 21.745L15.9915 26.7826C15.377 27.1334 14.623 27.1334 14.0086 26.7826L5.18295 21.745C4.55934 21.3891 4.1744 20.7261 4.1744 20.0081V9.99193C4.1744 9.27387 4.55934 8.61092 5.18295 8.25497L14.0086 3.21735Z" stroke="black" stroke-width="2"/>
+</svg>
diff --git a/apps/web/public/icons/icon/geo-octagon.svg b/apps/web/public/icons/icon/geo-octagon.svg
new file mode 100644
index 00000000..d357224b
--- /dev/null
+++ b/apps/web/public/icons/icon/geo-octagon.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M14.2421 3.22448C14.7278 3.02557 15.2722 3.02557 15.7579 3.22448L22.8403 6.12478C23.3311 6.32579 23.722 6.71354 23.927 7.20275L26.842 14.1602C27.0491 14.6546 27.0491 15.2115 26.842 15.7059L23.927 22.6633C23.722 23.1525 23.3311 23.5403 22.8403 23.7413L15.7579 26.6416C15.2722 26.8405 14.7278 26.8405 14.2421 26.6416L7.1597 23.7413C6.66885 23.5403 6.27795 23.1525 6.07299 22.6633L3.15802 15.7059C2.95088 15.2115 2.95088 14.6546 3.15802 14.1602L6.07299 7.20275C6.27795 6.71354 6.66885 6.32579 7.1597 6.12478L14.2421 3.22448Z" stroke="black" stroke-width="2"/>
+</svg>
diff --git a/apps/web/public/icons/icon/geo-oval.svg b/apps/web/public/icons/icon/geo-oval.svg
new file mode 100644
index 00000000..795ad348
--- /dev/null
+++ b/apps/web/public/icons/icon/geo-oval.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M15 3C19.8522 3 23 6.82114 23 11.8168L23 18.1832C23 23.1789 19.8522 27 15 27C10.1792 27 7.05169 23.2289 7 18.2767L7 11.8168C7 6.82114 10.1478 3 15 3Z" stroke="black" stroke-width="2"/>
+</svg>
diff --git a/apps/web/public/icons/icon/geo-pentagon.svg b/apps/web/public/icons/icon/geo-pentagon.svg
new file mode 100644
index 00000000..8f034393
--- /dev/null
+++ b/apps/web/public/icons/icon/geo-pentagon.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M13.8244 3.84017C14.5254 3.33088 15.4746 3.33088 16.1756 3.84017L26.2014 11.1244C26.9024 11.6337 27.1957 12.5364 26.928 13.3604L23.0984 25.1465C22.8307 25.9706 22.0628 26.5285 21.1963 26.5285H8.80369C7.93723 26.5285 7.16932 25.9706 6.90158 25.1465L3.07204 13.3604C2.80429 12.5364 3.09761 11.6337 3.79859 11.1244L13.8244 3.84017Z" stroke="black" stroke-width="2"/>
+</svg>
diff --git a/apps/web/public/icons/icon/geo-rectangle.svg b/apps/web/public/icons/icon/geo-rectangle.svg
new file mode 100644
index 00000000..52ab53b1
--- /dev/null
+++ b/apps/web/public/icons/icon/geo-rectangle.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M25 3C26.1046 3 27 3.89543 27 5V25C27 26.1046 26.1046 27 25 27H5C3.89543 27 3 26.1046 3 25V5C3 3.89543 3.89543 3 5 3H25Z" stroke="black" stroke-width="2"/>
+</svg>
diff --git a/apps/web/public/icons/icon/geo-rhombus-2.svg b/apps/web/public/icons/icon/geo-rhombus-2.svg
new file mode 100644
index 00000000..46937018
--- /dev/null
+++ b/apps/web/public/icons/icon/geo-rhombus-2.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M5.77115 3C4.48498 3 3.53316 4.19648 3.82237 5.44972L8.43775 25.4497C8.64717 26.3572 9.45523 27 10.3865 27H24.2288C25.515 27 26.4668 25.8035 26.1776 24.5503L21.5622 4.55028C21.3528 3.64282 20.5448 3 19.6135 3H5.77115Z" stroke="black" stroke-width="2"/>
+</svg>
diff --git a/apps/web/public/icons/icon/geo-rhombus.svg b/apps/web/public/icons/icon/geo-rhombus.svg
new file mode 100644
index 00000000..764b667a
--- /dev/null
+++ b/apps/web/public/icons/icon/geo-rhombus.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M24.2288 3C25.515 3 26.4668 4.19648 26.1776 5.44972L21.5622 25.4497C21.3528 26.3572 20.5448 27 19.6135 27H5.77115C4.48498 27 3.53316 25.8035 3.82237 24.5503L8.43776 4.55028C8.64717 3.64282 9.45523 3 10.3865 3H24.2288Z" stroke="black" stroke-width="2"/>
+</svg>
diff --git a/apps/web/public/icons/icon/geo-star.svg b/apps/web/public/icons/icon/geo-star.svg
new file mode 100644
index 00000000..cd2df657
--- /dev/null
+++ b/apps/web/public/icons/icon/geo-star.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M7.34666 17.7391L6.53767 18.327L7.34665 17.7391L3.23417 12.0794L9.88794 9.91783C10.2897 9.78732 10.6398 9.53294 10.8881 9.19117L15 3.53101L19.1119 9.19117L19.9209 8.60343L19.1119 9.19118C19.3602 9.53294 19.7103 9.78732 20.1121 9.91783L26.7658 12.0794L22.6533 17.7391C22.405 18.0809 22.2713 18.4924 22.2713 18.9149L22.2717 25.911L15.6181 23.7487L15.3091 24.6997L15.6181 23.7487C15.2164 23.6181 14.7836 23.6181 14.3819 23.7487L14.6909 24.6997L14.3819 23.7487L7.72831 25.911L7.72868 18.9149C7.7287 18.4925 7.59497 18.0809 7.34666 17.7391Z" stroke="black" stroke-width="2"/>
+</svg>
diff --git a/apps/web/public/icons/icon/geo-trapezoid.svg b/apps/web/public/icons/icon/geo-trapezoid.svg
new file mode 100644
index 00000000..053abe21
--- /dev/null
+++ b/apps/web/public/icons/icon/geo-trapezoid.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M19.6135 3C20.5448 3 21.3528 3.64282 21.5622 4.55028L26.1776 24.5503C26.4668 25.8035 25.515 27 24.2288 27H5.77115C4.48498 27 3.53316 25.8035 3.82237 24.5503L8.43776 4.55028C8.64717 3.64282 9.45523 3 10.3865 3H19.6135Z" stroke="black" stroke-width="2"/>
+</svg>
diff --git a/apps/web/public/icons/icon/geo-triangle.svg b/apps/web/public/icons/icon/geo-triangle.svg
new file mode 100644
index 00000000..aad8aae4
--- /dev/null
+++ b/apps/web/public/icons/icon/geo-triangle.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M27.55 26L2.45007 26L15 4L27.55 26Z" stroke="black" stroke-width="2" stroke-linejoin="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/geo-x-box.svg b/apps/web/public/icons/icon/geo-x-box.svg
new file mode 100644
index 00000000..d1aaf5ec
--- /dev/null
+++ b/apps/web/public/icons/icon/geo-x-box.svg
@@ -0,0 +1,5 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M25 3C26.1046 3 27 3.89543 27 5V25C27 26.1046 26.1046 27 25 27H5C3.89543 27 3 26.1046 3 25V5C3 3.89543 3.89543 3 5 3H25Z" stroke="black" stroke-width="2"/>
+<path d="M8 8L22 22" stroke="black" stroke-width="2" stroke-linecap="round"/>
+<path d="M22 8L8 22" stroke="black" stroke-width="2" stroke-linecap="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/github.svg b/apps/web/public/icons/icon/github.svg
new file mode 100644
index 00000000..12461b1c
--- /dev/null
+++ b/apps/web/public/icons/icon/github.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M14.9987 0.500017C6.99271 0.500017 0.5 6.99187 0.5 15.0005C0.5 21.406 4.6543 26.8412 10.4162 28.7594C11.1417 28.8921 11.406 28.4443 11.406 28.0598C11.406 27.7153 11.3936 26.8038 11.3865 25.5941C7.35322 26.47 6.50225 23.65 6.50225 23.65C5.84265 21.9748 4.89198 21.5288 4.89198 21.5288C3.57545 20.6298 4.99167 20.6476 4.99167 20.6476C6.44706 20.7499 7.21258 22.1421 7.21258 22.1421C8.50596 24.3577 10.6067 23.7177 11.4328 23.3465C11.5645 22.41 11.9392 21.7709 12.3532 21.4086C9.13351 21.0419 5.7483 19.7984 5.7483 14.2421C5.7483 12.6585 6.31354 11.3652 7.24107 10.3513C7.09152 9.98454 6.59393 8.51043 7.38349 6.51383C7.38349 6.51383 8.60031 6.12395 11.3704 7.99948C12.5267 7.67814 13.7676 7.51791 15.0004 7.51168C16.2324 7.51791 17.4724 7.67814 18.6305 7.99948C21.3988 6.12395 22.6138 6.51383 22.6138 6.51383C23.4052 8.51043 22.9076 9.98454 22.7589 10.3513C23.6883 11.3652 24.249 12.6585 24.249 14.2421C24.249 19.8126 20.8585 21.0383 17.629 21.3971C18.1489 21.8448 18.6127 22.7296 18.6127 24.0826C18.6127 26.0205 18.5948 27.5845 18.5948 28.0598C18.5948 28.4479 18.8566 28.8992 19.5918 28.7577C25.3493 26.8358 29.5 21.4051 29.5 15.0005C29.5 6.99187 23.0073 0.500017 14.9987 0.500017" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/group.svg b/apps/web/public/icons/icon/group.svg
new file mode 100644
index 00000000..e84d1966
--- /dev/null
+++ b/apps/web/public/icons/icon/group.svg
@@ -0,0 +1,10 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M2.8999 1.8999C2.63468 1.8999 2.38033 2.00526 2.19279 2.1928C2.00526 2.38034 1.8999 2.63469 1.8999 2.89991L1.89993 6.89991C1.89994 7.45219 2.34766 7.89991 2.89994 7.8999C3.45223 7.8999 3.89994 7.45218 3.89993 6.89989L3.89991 3.8999H6.89993C7.45222 3.8999 7.89993 3.45219 7.89993 2.8999C7.89993 2.34762 7.45222 1.8999 6.89993 1.8999H2.8999Z" fill="black"/>
+<path d="M11.8999 1.8999C11.3476 1.8999 10.8999 2.34762 10.8999 2.8999C10.8999 3.45219 11.3476 3.8999 11.8999 3.8999H17.8999C18.4522 3.8999 18.8999 3.45219 18.8999 2.8999C18.8999 2.34762 18.4522 1.8999 17.8999 1.8999H11.8999Z" fill="black"/>
+<path d="M10.8999 26.8999C10.8999 26.3476 11.3476 25.8999 11.8999 25.8999H17.8999C18.4522 25.8999 18.8999 26.3476 18.8999 26.8999C18.8999 27.4522 18.4522 27.8999 17.8999 27.8999H11.8999C11.3476 27.8999 10.8999 27.4522 10.8999 26.8999Z" fill="black"/>
+<path d="M3.8999 11.8999C3.8999 11.3476 3.45219 10.8999 2.8999 10.8999C2.34762 10.8999 1.8999 11.3476 1.8999 11.8999V17.8999C1.8999 18.4522 2.34762 18.8999 2.8999 18.8999C3.45219 18.8999 3.8999 18.4522 3.8999 17.8999V11.8999Z" fill="black"/>
+<path d="M26.8999 10.8999C27.4522 10.8999 27.8999 11.3476 27.8999 11.8999V17.8999C27.8999 18.4522 27.4522 18.8999 26.8999 18.8999C26.3476 18.8999 25.8999 18.4522 25.8999 17.8999V11.8999C25.8999 11.3476 26.3476 10.8999 26.8999 10.8999Z" fill="black"/>
+<path d="M22.8999 1.8999C22.3476 1.8999 21.8999 2.34762 21.8999 2.8999C21.8999 3.45219 22.3476 3.8999 22.8999 3.8999H25.8999V6.8999C25.8999 7.45219 26.3476 7.8999 26.8999 7.8999C27.4522 7.8999 27.8999 7.45219 27.8999 6.8999V2.8999C27.8999 2.34762 27.4504 1.8999 26.8981 1.8999H22.8999Z" fill="black"/>
+<path d="M2.8999 21.8999C3.45219 21.8999 3.8999 22.3476 3.8999 22.8999V25.8999H6.89993C7.45222 25.8999 7.89993 26.3476 7.89993 26.8999C7.89993 27.4522 7.45222 27.8999 6.89993 27.8999H2.8999C2.34762 27.8999 1.8999 27.4522 1.8999 26.8999V22.8999C1.8999 22.3476 2.34762 21.8999 2.8999 21.8999Z" fill="black"/>
+<path d="M27.8999 22.8999C27.8999 22.3476 27.4522 21.8999 26.8999 21.8999C26.3476 21.8999 25.8999 22.3476 25.8999 22.8999L25.8982 25.8999H22.8999C22.3476 25.8999 21.8999 26.3476 21.8999 26.8999C21.8999 27.4522 22.3476 27.8999 22.8999 27.8999H26.8999C27.4522 27.8999 27.8999 27.4522 27.8999 26.8999L27.8999 22.8999Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/horizontal-align-end.svg b/apps/web/public/icons/icon/horizontal-align-end.svg
new file mode 100644
index 00000000..c929995d
--- /dev/null
+++ b/apps/web/public/icons/icon/horizontal-align-end.svg
@@ -0,0 +1,5 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect x="19.661" y="4.41943" width="2" height="22" rx="1" fill="black"/>
+<rect x="16.571" y="15.4194" width="2" height="8" rx="1" transform="rotate(135 16.571 15.4194)" fill="black"/>
+<rect x="15.1569" y="14.0052" width="2" height="8" rx="1" transform="rotate(45 15.1569 14.0052)" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/horizontal-align-middle.svg b/apps/web/public/icons/icon/horizontal-align-middle.svg
new file mode 100644
index 00000000..d36bf1ca
--- /dev/null
+++ b/apps/web/public/icons/icon/horizontal-align-middle.svg
@@ -0,0 +1,7 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect x="19.25" y="15" width="2" height="8" rx="1" transform="rotate(-45 19.25 15)" fill="black"/>
+<rect x="20.6642" y="16.4142" width="2" height="8" rx="1" transform="rotate(-135 20.6642 16.4142)" fill="black"/>
+<rect x="11.071" y="15" width="2" height="8" rx="1" transform="rotate(135 11.071 15)" fill="black"/>
+<rect x="9.65686" y="13.5858" width="2" height="8" rx="1" transform="rotate(45 9.65686 13.5858)" fill="black"/>
+<rect x="16.16" y="26" width="2" height="22" rx="1" transform="rotate(180 16.16 26)" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/horizontal-align-start.svg b/apps/web/public/icons/icon/horizontal-align-start.svg
new file mode 100644
index 00000000..895d0482
--- /dev/null
+++ b/apps/web/public/icons/icon/horizontal-align-start.svg
@@ -0,0 +1,5 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect x="10.4972" y="26.5814" width="2" height="22" rx="1" transform="rotate(-180 10.4972 26.5814)" fill="black"/>
+<rect x="13.5889" y="15.5814" width="2" height="8" rx="1" transform="rotate(-45 13.5889 15.5814)" fill="black"/>
+<rect x="15.0031" y="16.9956" width="2" height="8" rx="1" transform="rotate(-135 15.0031 16.9956)" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/info-circle.svg b/apps/web/public/icons/icon/info-circle.svg
new file mode 100644
index 00000000..696a14b8
--- /dev/null
+++ b/apps/web/public/icons/icon/info-circle.svg
@@ -0,0 +1,5 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M14.9998 27.2949C21.7904 27.2949 27.2954 21.7899 27.2954 14.9993C27.2954 8.20858 21.7904 2.70361 14.9998 2.70361C8.20907 2.70361 2.7041 8.20858 2.7041 14.9993C2.7041 21.7899 8.20907 27.2949 14.9998 27.2949Z" stroke="black" stroke-width="2"/>
+<path d="M13 21H15M17 21H15M15 21V13H13" stroke="black" stroke-width="2" stroke-linecap="square" stroke-linejoin="round"/>
+<circle cx="14.9998" cy="9" r="1.5" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/leading.svg b/apps/web/public/icons/icon/leading.svg
new file mode 100644
index 00000000..6b661e8d
--- /dev/null
+++ b/apps/web/public/icons/icon/leading.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M26 15C26 16.7708 24.9234 18.4963 22.9306 19.8248C20.9448 21.1487 18.1438 22 15 22C11.8562 22 9.05522 21.1487 7.06942 19.8248C5.07663 18.4963 4 16.7708 4 15C4 13.2292 5.07663 11.5037 7.06942 10.1752C9.05522 8.85133 11.8562 8 15 8C18.1438 8 20.9448 8.85133 22.9306 10.1752C24.9234 11.5037 26 13.2292 26 15Z" stroke="black" stroke-width="2"/>
+<path d="M25 15C25 17.2091 23.433 19 21.5 19C19.567 19 18 17.2091 18 15C18 12.7909 19.567 11 21.5 11C23.433 11 25 12.7909 25 15Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/link.svg b/apps/web/public/icons/icon/link.svg
new file mode 100644
index 00000000..83dd1b9f
--- /dev/null
+++ b/apps/web/public/icons/icon/link.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M11 9H9.25C8.08836 9 7.50754 9 7.02455 9.09223C5.0411 9.47098 3.49061 10.9595 3.09607 12.8636C3 13.3272 3 13.8848 3 15C3 16.1152 3 16.6728 3.09607 17.1364C3.49061 19.0405 5.0411 20.529 7.02455 20.9078C7.50754 21 8.08836 21 9.25 21H11M19 9H20.75C21.9116 9 22.4925 9 22.9755 9.09223C24.9589 9.47098 26.5094 10.9595 26.9039 12.8636C27 13.3272 27 13.8848 27 15C27 16.1152 27 16.6728 26.9039 17.1364C26.5094 19.0405 24.9589 20.529 22.9755 20.9078C22.4925 21 21.9116 21 20.75 21H19" stroke="black" stroke-width="2" stroke-linecap="round"/>
+<path d="M10.0015 15H20.0015" stroke="black" stroke-width="2" stroke-linecap="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/lock.svg b/apps/web/public/icons/icon/lock.svg
new file mode 100644
index 00000000..5693cc4c
--- /dev/null
+++ b/apps/web/public/icons/icon/lock.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M5 14.0215C5 13.4692 5.44772 13.0215 6 13.0215H24C24.5523 13.0215 25 13.4692 25 14.0215V26.0215C25 26.5738 24.5523 27.0215 24 27.0215H6C5.44772 27.0215 5 26.5738 5 26.0215V14.0215Z" stroke="black" stroke-width="2" stroke-linejoin="round"/>
+<path d="M21 13.0219V9.28841C21 5.42747 18.8637 2.97851 15.0028 2.97851V2.97851V2.97851C11.1423 2.97851 9 5.43346 9 9.29393V13.0219" stroke="black" stroke-width="2"/>
+</svg>
diff --git a/apps/web/public/icons/icon/menu.svg b/apps/web/public/icons/icon/menu.svg
new file mode 100644
index 00000000..0ee03f0f
--- /dev/null
+++ b/apps/web/public/icons/icon/menu.svg
@@ -0,0 +1,5 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M3.92308 6C3.41328 6 3 6.44772 3 7C3 7.55228 3.41328 8 3.92308 8H26.0769C26.5867 8 27 7.55228 27 7C27 6.44772 26.5867 6 26.0769 6H3.92308Z" fill="black"/>
+<path d="M3 15C3 14.4477 3.41328 14 3.92308 14H26.0769C26.5867 14 27 14.4477 27 15C27 15.5523 26.5867 16 26.0769 16H3.92308C3.41328 16 3 15.5523 3 15Z" fill="black"/>
+<path d="M3 23C3 22.4477 3.41328 22 3.92308 22H26.0769C26.5867 22 27 22.4477 27 23C27 23.5523 26.5867 24 26.0769 24H3.92308C3.41328 24 3 23.5523 3 23Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/minus.svg b/apps/web/public/icons/icon/minus.svg
new file mode 100644
index 00000000..2b208b66
--- /dev/null
+++ b/apps/web/public/icons/icon/minus.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M4.5 15C4.5 14.4477 4.94772 14 5.5 14H24.5C25.0523 14 25.5 14.4477 25.5 15C25.5 15.5523 25.0523 16 24.5 16H5.5C4.94772 16 4.5 15.5523 4.5 15Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/mixed.svg b/apps/web/public/icons/icon/mixed.svg
new file mode 100644
index 00000000..b5d50a2f
--- /dev/null
+++ b/apps/web/public/icons/icon/mixed.svg
@@ -0,0 +1,11 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M27.25 14.5C27.25 15.7426 26.2426 16.75 25 16.75C23.7574 16.75 22.75 15.7426 22.75 14.5C22.75 13.2574 23.7574 12.25 25 12.25C26.2426 12.25 27.25 13.2574 27.25 14.5Z" fill="black"/>
+<path d="M7.25 14.5C7.25 15.7426 6.24264 16.75 5 16.75C3.75736 16.75 2.75 15.7426 2.75 14.5C2.75 13.2574 3.75736 12.25 5 12.25C6.24264 12.25 7.25 13.2574 7.25 14.5Z" fill="black"/>
+<path d="M17.25 4.5C17.25 5.74264 16.2426 6.75 15 6.75C13.7574 6.75 12.75 5.74264 12.75 4.5C12.75 3.25736 13.7574 2.25 15 2.25C16.2426 2.25 17.25 3.25736 17.25 4.5Z" fill="black"/>
+<path d="M17.25 24.5C17.25 25.7426 16.2426 26.75 15 26.75C13.7574 26.75 12.75 25.7426 12.75 24.5C12.75 23.2574 13.7574 22.25 15 22.25C16.2426 22.25 17.25 23.2574 17.25 24.5Z" fill="black"/>
+<path d="M6.5 4.75C6.5 5.57843 5.82843 6.25 5 6.25C4.17157 6.25 3.5 5.57843 3.5 4.75C3.5 3.92157 4.17157 3.25 5 3.25C5.82843 3.25 6.5 3.92157 6.5 4.75Z" fill="black"/>
+<path d="M6.5 24.75C6.5 25.5784 5.82843 26.25 5 26.25C4.17157 26.25 3.5 25.5784 3.5 24.75C3.5 23.9216 4.17157 23.25 5 23.25C5.82843 23.25 6.5 23.9216 6.5 24.75Z" fill="black"/>
+<path d="M16.5 14.5C16.5 15.3284 15.8284 16 15 16C14.1716 16 13.5 15.3284 13.5 14.5C13.5 13.6716 14.1716 13 15 13C15.8284 13 16.5 13.6716 16.5 14.5Z" fill="black"/>
+<path d="M26.5 24.75C26.5 25.5784 25.8284 26.25 25 26.25C24.1716 26.25 23.5 25.5784 23.5 24.75C23.5 23.9216 24.1716 23.25 25 23.25C25.8284 23.25 26.5 23.9216 26.5 24.75Z" fill="black"/>
+<path d="M26.5 4.75C26.5 5.57843 25.8284 6.25 25 6.25C24.1716 6.25 23.5 5.57843 23.5 4.75C23.5 3.92157 24.1716 3.25 25 3.25C25.8284 3.25 26.5 3.92157 26.5 4.75Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/pack.svg b/apps/web/public/icons/icon/pack.svg
new file mode 100644
index 00000000..3095ae1c
--- /dev/null
+++ b/apps/web/public/icons/icon/pack.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M1 2C1 1.44772 1.44772 1 2 1H12C12.5523 1 13 1.44772 13 2V12C13 12.5523 12.5523 13 12 13H2C1.44772 13 1 12.5523 1 12V2ZM1 18C1 17.4477 1.44772 17 2 17H12C12.5523 17 13 17.4477 13 18V28C13 28.5523 12.5523 29 12 29H2C1.44772 29 1 28.5523 1 28V18ZM17 2C17 1.44772 17.4477 1 18 1H28C28.5523 1 29 1.44772 29 2V12C29 12.5523 28.5523 13 28 13H18C17.4477 13 17 12.5523 17 12V2ZM17 18C17 17.4477 17.4477 17 18 17H28C28.5523 17 29 17.4477 29 18V28C29 28.5523 28.5523 29 28 29H18C17.4477 29 17 28.5523 17 28V18Z" stroke="black" stroke-width="2"/>
+</svg>
diff --git a/apps/web/public/icons/icon/plus.svg b/apps/web/public/icons/icon/plus.svg
new file mode 100644
index 00000000..bb2dd71f
--- /dev/null
+++ b/apps/web/public/icons/icon/plus.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M16 5.5C16 4.94772 15.5523 4.5 15 4.5C14.4477 4.5 14 4.94772 14 5.5V14H5.5C4.94772 14 4.5 14.4477 4.5 15C4.5 15.5523 4.94772 16 5.5 16H14V24.5C14 25.0523 14.4477 25.5 15 25.5C15.5523 25.5 16 25.0523 16 24.5V16H24.5C25.0523 16 25.5 15.5523 25.5 15C25.5 14.4477 25.0523 14 24.5 14H16V5.5Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/question-mark-circle.svg b/apps/web/public/icons/icon/question-mark-circle.svg
new file mode 100644
index 00000000..eedb2cf7
--- /dev/null
+++ b/apps/web/public/icons/icon/question-mark-circle.svg
@@ -0,0 +1,5 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M14.9998 27.2951C21.7904 27.2951 27.2954 21.7901 27.2954 14.9995C27.2954 8.20876 21.7904 2.7038 14.9998 2.7038C8.20907 2.7038 2.7041 8.20876 2.7041 14.9995C2.7041 21.7901 8.20907 27.2951 14.9998 27.2951Z" stroke="black" stroke-width="2"/>
+<circle cx="14.9998" cy="21" r="1.5" fill="black"/>
+<path d="M11 12.5C11 10.5 12.7 8.75 15 8.75C17.3 8.75 19 10.5 19 12.5C19 15.69 15 15.65 15 17.4" stroke="black" stroke-width="2.2" stroke-linecap="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/question-mark.svg b/apps/web/public/icons/icon/question-mark.svg
new file mode 100644
index 00000000..ef5ecf4f
--- /dev/null
+++ b/apps/web/public/icons/icon/question-mark.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M9 8.20017C9 5.03875 12.0386 2.7002 15 2.7002C17.9614 2.7002 21 5.03875 21 8.20017C21 13.7033 15 13.5452 15 19.0002" stroke="black" stroke-width="2.3" stroke-linecap="round"/>
+<path d="M16.75 24.9648C16.75 25.9313 15.9665 26.7148 15 26.7148C14.0335 26.7148 13.25 25.9313 13.25 24.9648C13.25 23.9983 14.0335 23.2148 15 23.2148C15.9665 23.2148 16.75 23.9983 16.75 24.9648Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/redo.svg b/apps/web/public/icons/icon/redo.svg
new file mode 100644
index 00000000..cdb6c7e9
--- /dev/null
+++ b/apps/web/public/icons/icon/redo.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M20.2929 4.29289C20.6834 3.90237 21.3166 3.90237 21.7071 4.29289L25.7071 8.29289C26.0976 8.68342 26.0976 9.31658 25.7071 9.70711L21.7071 13.7071C21.3166 14.0976 20.6834 14.0976 20.2929 13.7071C19.9024 13.3166 19.9024 12.6834 20.2929 12.2929L22.5858 10H12C8.13401 10 5 13.134 5 17C5 20.866 8.13401 24 12 24H20C20.5523 24 21 24.4477 21 25C21 25.5523 20.5523 26 20 26H12C7.02944 26 3 21.9706 3 17C3 12.0294 7.02944 8 12 8H22.5858L20.2929 5.70711C19.9024 5.31658 19.9024 4.68342 20.2929 4.29289Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/reset-zoom.svg b/apps/web/public/icons/icon/reset-zoom.svg
new file mode 100644
index 00000000..407f6557
--- /dev/null
+++ b/apps/web/public/icons/icon/reset-zoom.svg
@@ -0,0 +1,6 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="12.4077" cy="12.4077" r="9.40773" stroke="black" stroke-width="2"/>
+<path d="M27.0801 25.2404C27.5881 25.7484 27.5881 26.5721 27.0801 27.0802C26.572 27.5883 25.7483 27.5883 25.2402 27.0802L18.8888 20.7288C18.3861 20.2261 18.38 19.413 18.8752 18.9028C19.3809 18.3817 20.2152 18.3755 20.7287 18.8889L27.0801 25.2404Z" fill="black"/>
+<path d="M7.90771 12.4077C7.90771 14.893 9.92243 16.9077 12.4077 16.9077C14.893 16.9077 16.9077 14.893 16.9077 12.4077C16.9077 9.92243 14.893 7.90771 12.4077 7.90771" stroke="black" stroke-width="2" stroke-linecap="round"/>
+<circle cx="9.21625" cy="9.15527" r="1" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/rotate-ccw.svg b/apps/web/public/icons/icon/rotate-ccw.svg
new file mode 100644
index 00000000..14a5533b
--- /dev/null
+++ b/apps/web/public/icons/icon/rotate-ccw.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="31" viewBox="0 0 30 31" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M15.5968 7.18641C15.761 7.31413 16.0002 7.19711 16.0002 6.98907V4.99998C19.9629 5.00001 22.3696 5.72742 23.8211 7.17892C25.2726 8.63045 26 11.0372 26 15C26 15.5523 26.4477 16 27 16C27.5523 16 28 15.5523 28 15C28 10.8876 27.265 7.79439 25.2353 5.7647C23.2056 3.73506 20.1125 3.00001 16.0002 2.99998V1.01139C16.0002 0.803351 15.761 0.686328 15.5968 0.81405L11.754 3.80289C11.6253 3.90298 11.6253 4.09748 11.754 4.19757L15.5968 7.18641Z" fill="black"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M3.5 9.00002C2.67157 9.00002 2 9.67159 2 10.5V27.5C2 28.3284 2.67157 29 3.5 29H20.5C21.3284 29 22 28.3284 22 27.5V10.5C22 9.67159 21.3284 9.00002 20.5 9.00002H3.5ZM4 11V27H20V11H4Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/rotate-cw.svg b/apps/web/public/icons/icon/rotate-cw.svg
new file mode 100644
index 00000000..ba320383
--- /dev/null
+++ b/apps/web/public/icons/icon/rotate-cw.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M14.4032 7.21699C14.239 7.34471 13.9998 7.22768 13.9998 7.01965V5.03056C10.0371 5.03059 7.63043 5.75799 6.17893 7.2095C4.7274 8.66103 4 11.0678 4 15.0306C4 15.5828 3.55228 16.0306 3 16.0306C2.44772 16.0306 2 15.5828 2 15.0306C2 10.9182 2.73503 7.82497 4.76472 5.79528C6.79436 3.76564 9.88751 3.03059 13.9998 3.03056V1.04197C13.9998 0.83393 14.239 0.716906 14.4032 0.844629L18.246 3.83347C18.3747 3.93356 18.3747 4.12806 18.246 4.22815L14.4032 7.21699Z" fill="black"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M26.5 9.03059C27.3284 9.03059 28 9.70217 28 10.5306V27.5306C28 28.359 27.3284 29.0306 26.5 29.0306H9.5C8.67157 29.0306 8 28.359 8 27.5306V10.5306C8 9.70217 8.67157 9.03059 9.5 9.03059H26.5ZM26 11.0306V27.0306H10V11.0306H26Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/send-backward.svg b/apps/web/public/icons/icon/send-backward.svg
new file mode 100644
index 00000000..917b9a1a
--- /dev/null
+++ b/apps/web/public/icons/icon/send-backward.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M21.4998 17.5002L14.9998 24.0002M14.9998 24.0002L8.49976 17.5002M14.9998 24.0002L14.9998 6" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/send-to-back.svg b/apps/web/public/icons/icon/send-to-back.svg
new file mode 100644
index 00000000..3046efbc
--- /dev/null
+++ b/apps/web/public/icons/icon/send-to-back.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M21.4998 14.5002L14.9998 21.0002M14.9998 21.0002L8.49976 14.5002M14.9998 21.0002L14.9998 3M27 27H3" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/share-1.svg b/apps/web/public/icons/icon/share-1.svg
new file mode 100644
index 00000000..160f0418
--- /dev/null
+++ b/apps/web/public/icons/icon/share-1.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M11 15C11 17.2091 9.20914 19 7 19C4.79086 19 3 17.2091 3 15C3 12.7909 4.79086 11 7 11C9.20914 11 11 12.7909 11 15ZM27 7C27 9.20914 25.2091 11 23 11C20.7909 11 19 9.20914 19 7C19 4.79086 20.7909 3 23 3C25.2091 3 27 4.79086 27 7ZM27 23C27 25.2091 25.2091 27 23 27C20.7909 27 19 25.2091 19 23C19 20.7909 20.7909 19 23 19C25.2091 19 27 20.7909 27 23Z" stroke="black" stroke-width="2"/>
+<path d="M19 21L11 16M19 9L11 14" stroke="black" stroke-width="2"/>
+</svg>
diff --git a/apps/web/public/icons/icon/size-extra-large.svg b/apps/web/public/icons/icon/size-extra-large.svg
new file mode 100644
index 00000000..408e3bb6
--- /dev/null
+++ b/apps/web/public/icons/icon/size-extra-large.svg
@@ -0,0 +1,4 @@
+<svg width="31" height="30" viewBox="0 0 31 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M4.4209 5.7915L8.05078 11.9263H8.19141L11.8389 5.7915H16.1367L10.6436 14.7915L16.2598 23.7915H11.8828L8.19141 17.6479H8.05078L4.35938 23.7915H0L5.63379 14.7915L0.105469 5.7915H4.4209Z" fill="black"/>
+<path d="M18.5449 23.7915V5.7915H22.3506V20.6538H30.0674V23.7915H18.5449Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/size-large.svg b/apps/web/public/icons/icon/size-large.svg
new file mode 100644
index 00000000..011e0f2b
--- /dev/null
+++ b/apps/web/public/icons/icon/size-large.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M9 23.7915V5.7915H12.8057V20.6538H20.5225V23.7915H9Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/size-medium.svg b/apps/web/public/icons/icon/size-medium.svg
new file mode 100644
index 00000000..c5de337c
--- /dev/null
+++ b/apps/web/public/icons/icon/size-medium.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M5 5.7915H9.69336L14.6504 17.8853H14.8613L19.8184 5.7915H24.5117V23.7915H20.8203V12.0757H20.6709L16.0127 23.7036H13.499L8.84082 12.0317H8.69141V23.7915H5V5.7915Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/size-small.svg b/apps/web/public/icons/icon/size-small.svg
new file mode 100644
index 00000000..68c4ac46
--- /dev/null
+++ b/apps/web/public/icons/icon/size-small.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M18.0646 11.0675C17.9962 10.3777 17.7026 9.84186 17.1838 9.45991C16.6651 9.07796 15.961 8.88699 15.0717 8.88699C14.4675 8.88699 13.9572 8.9725 13.5411 9.14352C13.1249 9.30884 12.8057 9.53972 12.5834 9.83616C12.3667 10.1326 12.2584 10.4689 12.2584 10.8452C12.247 11.1587 12.3126 11.4324 12.4551 11.6661C12.6033 11.8998 12.8057 12.1022 13.0622 12.2732C13.3188 12.4385 13.6152 12.5839 13.9515 12.7093C14.2879 12.829 14.647 12.9316 15.029 13.0172L16.6024 13.3934C17.3663 13.5644 18.0675 13.7925 18.7059 14.0775C19.3444 14.3625 19.8974 14.7131 20.3648 15.1293C20.8323 15.5454 21.1943 16.0357 21.4508 16.6001C21.7131 17.1644 21.847 17.8115 21.8527 18.5411C21.847 19.6129 21.5734 20.5421 21.0318 21.3288C20.496 22.1098 19.7207 22.7169 18.7059 23.1502C17.6969 23.5777 16.4798 23.7915 15.0546 23.7915C13.6409 23.7915 12.4095 23.5749 11.3606 23.1416C10.3173 22.7084 9.50214 22.067 8.91496 21.2176C8.33349 20.3625 8.0285 19.305 8 18.0452H11.5829C11.6228 18.6324 11.791 19.1226 12.0874 19.516C12.3895 19.9036 12.7915 20.1972 13.2931 20.3967C13.8005 20.5906 14.3734 20.6875 15.0119 20.6875C15.639 20.6875 16.1834 20.5963 16.6451 20.4138C17.1126 20.2314 17.4746 19.9777 17.7311 19.6528C17.9877 19.3278 18.1159 18.9544 18.1159 18.5326C18.1159 18.1392 17.9991 17.8086 17.7653 17.5407C17.5373 17.2727 17.201 17.0447 16.7563 16.8566C16.3173 16.6685 15.7786 16.4974 15.1401 16.3435L13.2333 15.8647C11.7568 15.5055 10.591 14.944 9.73587 14.1801C8.88076 13.4162 8.45606 12.3872 8.46176 11.0932C8.45606 10.0328 8.73824 9.10647 9.30831 8.31407C9.88409 7.52167 10.6736 6.90314 11.677 6.45849C12.6803 6.01383 13.8204 5.7915 15.0974 5.7915C16.3972 5.7915 17.5316 6.01383 18.5007 6.45849C19.4755 6.90314 20.2337 7.52167 20.7753 8.31407C21.3169 9.10647 21.5962 10.0243 21.6133 11.0675H18.0646Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/spline-cubic.svg b/apps/web/public/icons/icon/spline-cubic.svg
new file mode 100644
index 00000000..480fe6e1
--- /dev/null
+++ b/apps/web/public/icons/icon/spline-cubic.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M6 6V8C6 13.6005 6 16.4008 7.08993 18.5399C8.04867 20.4215 9.57847 21.9513 11.4601 22.9101C13.5992 24 16.3995 24 22 24H24" stroke="black" stroke-width="2" stroke-linecap="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/spline-line.svg b/apps/web/public/icons/icon/spline-line.svg
new file mode 100644
index 00000000..bdbdf302
--- /dev/null
+++ b/apps/web/public/icons/icon/spline-line.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M6 6V24H24" stroke="black" stroke-width="2" stroke-linecap="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/stack-horizontal.svg b/apps/web/public/icons/icon/stack-horizontal.svg
new file mode 100644
index 00000000..2b5d782b
--- /dev/null
+++ b/apps/web/public/icons/icon/stack-horizontal.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M3 28V2M11 28V2M19 28V2M27 28V2" stroke="black" stroke-width="2.6" stroke-linecap="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/stack-vertical.svg b/apps/web/public/icons/icon/stack-vertical.svg
new file mode 100644
index 00000000..51c48dba
--- /dev/null
+++ b/apps/web/public/icons/icon/stack-vertical.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M28 27H2M28 19H2M28 11H2M28 3L2 3" stroke="black" stroke-width="2.6" stroke-linecap="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/stretch-horizontal.svg b/apps/web/public/icons/icon/stretch-horizontal.svg
new file mode 100644
index 00000000..2356c223
--- /dev/null
+++ b/apps/web/public/icons/icon/stretch-horizontal.svg
@@ -0,0 +1,5 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M1.99976 12H27.9998V18H1.99976V12Z" fill="black"/>
+<path d="M0 3C0 2.44772 0.447715 2 1 2C1.55228 2 2 2.44772 2 3V27C2 27.5523 1.55228 28 1 28C0.447715 28 0 27.5523 0 27V3Z" fill="black"/>
+<path d="M27.9998 3C27.9998 2.44772 28.4475 2 28.9998 2C29.552 2 29.9998 2.44772 29.9998 3V27C29.9998 27.5523 29.552 28 28.9998 28C28.4475 28 27.9998 27.5523 27.9998 27V3Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/stretch-vertical.svg b/apps/web/public/icons/icon/stretch-vertical.svg
new file mode 100644
index 00000000..5fb5d7c3
--- /dev/null
+++ b/apps/web/public/icons/icon/stretch-vertical.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M1.99988 1C1.99988 0.447715 2.44759 0 2.99988 0H26.9999C27.5522 0 27.9999 0.447715 27.9999 1C27.9999 1.55228 27.5522 2 26.9999 2H2.99988C2.44759 2 1.99988 1.55228 1.99988 1Z" fill="black"/>
+<path d="M12.0001 2.00001L18.0001 2.00001V28L26.9999 28C27.5522 28 27.9999 28.4477 27.9999 29C27.9999 29.5523 27.5522 30 26.9999 30H2.99988C2.44759 30 1.99988 29.5523 1.99988 29C1.99988 28.4477 2.44759 28 2.99988 28L12.0001 28V2.00001Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/text-align-center.svg b/apps/web/public/icons/icon/text-align-center.svg
new file mode 100644
index 00000000..31f653f2
--- /dev/null
+++ b/apps/web/public/icons/icon/text-align-center.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M7 21H23M9 15H21M5 9H25" stroke="black" stroke-width="2" stroke-linecap="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/text-align-left.svg b/apps/web/public/icons/icon/text-align-left.svg
new file mode 100644
index 00000000..14070fea
--- /dev/null
+++ b/apps/web/public/icons/icon/text-align-left.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M5 21H21M5 15H15M5 9H25" stroke="black" stroke-width="2" stroke-linecap="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/text-align-right.svg b/apps/web/public/icons/icon/text-align-right.svg
new file mode 100644
index 00000000..eb3f3690
--- /dev/null
+++ b/apps/web/public/icons/icon/text-align-right.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M9 21H25M15 15H25M5 9H25" stroke="black" stroke-width="2" stroke-linecap="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/toggle-off.svg b/apps/web/public/icons/icon/toggle-off.svg
new file mode 100644
index 00000000..e71190a9
--- /dev/null
+++ b/apps/web/public/icons/icon/toggle-off.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect opacity="0.32" x="1" y="7" width="28" height="18" rx="9" stroke="black" stroke-width="2"/>
+<circle opacity="0.32" cx="10" cy="16" r="6" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/toggle-on.svg b/apps/web/public/icons/icon/toggle-on.svg
new file mode 100644
index 00000000..554dbf4e
--- /dev/null
+++ b/apps/web/public/icons/icon/toggle-on.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="20" cy="16" r="6" fill="black"/>
+<rect x="1" y="7" width="28" height="18" rx="9" stroke="black" stroke-width="2"/>
+</svg>
diff --git a/apps/web/public/icons/icon/tool-arrow.svg b/apps/web/public/icons/icon/tool-arrow.svg
new file mode 100644
index 00000000..4ec0239c
--- /dev/null
+++ b/apps/web/public/icons/icon/tool-arrow.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M24 18.4667L24 6.00001M24 6.00001L11.5333 6.00001M24 6.00001L7.00002 23" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/tool-eraser.svg b/apps/web/public/icons/icon/tool-eraser.svg
new file mode 100644
index 00000000..684f5062
--- /dev/null
+++ b/apps/web/public/icons/icon/tool-eraser.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M6.73608 13.6608L3.30691 17.09C2.52587 17.871 2.52587 19.1374 3.30692 19.9184L8.61022 25.2217C10.5628 27.1743 13.7287 27.1743 15.6813 25.2217L16.9891 23.9139M6.73608 13.6608L17.8542 2.54266L28.1073 12.7957L16.9891 23.9139M6.73608 13.6608L16.9891 23.9139" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/tool-frame.svg b/apps/web/public/icons/icon/tool-frame.svg
new file mode 100644
index 00000000..a3f0ce44
--- /dev/null
+++ b/apps/web/public/icons/icon/tool-frame.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M10.2 27H5C3.89543 27 3 26.1046 3 25V19.8M19.8 27H25C26.1046 27 27 26.1046 27 25V19.8M3 10.2V5C3 3.89543 3.89543 3 5 3H10.2M19.8 3H25C26.1046 3 27 3.89543 27 5V10.2" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/tool-hand.svg b/apps/web/public/icons/icon/tool-hand.svg
new file mode 100644
index 00000000..f38c7bb0
--- /dev/null
+++ b/apps/web/public/icons/icon/tool-hand.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M8.63587 16.0218C8.44552 15.2986 8.25517 14.4043 7.85544 13.0722C7.09942 10.5825 5.78137 8.27199 5.13345 5.74583C4.70752 3.93612 6.2521 2.10299 8.17903 2.77722C11.1279 3.81302 12.0474 8.88558 12.4619 11.4547L12.6142 12.311C12.4106 8.60372 12.1453 6.1853 12.6142 3.13878C13.0768 0.248065 17.5022 0.313862 17.8678 3.17684C18.0568 5.25596 18.0581 7.33084 18.0581 9.41852C18.0581 9.8562 18.0581 12.5013 18.0962 12.2159C18.2104 10.9599 18.2675 6.14545 18.7434 4.71823C19.7824 1.52792 23.6363 2.68984 23.8257 5.59359C23.9832 7.69327 23.7641 9.8847 23.7115 11.9875C23.7115 12.0636 23.6924 12.5584 23.7495 12.33C24.3238 10.544 23.8257 5.59359 26.9373 6.21128C27.9639 6.41508 29.8685 7.80123 28.4794 12.5C26.5191 19.131 23.9795 21.9981 23.9795 29H9.97947C9.97947 24 3.36858 20.1796 1.47943 16.4024C1.06067 15.6031 0.852976 14.5946 1.11946 13.8714C1.55727 12.7487 2.37339 12.1588 3.6868 12.292C5.8293 12.4979 6.69503 14.3244 8.63587 16.0218Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/tool-highlight.svg b/apps/web/public/icons/icon/tool-highlight.svg
new file mode 100644
index 00000000..768600a3
--- /dev/null
+++ b/apps/web/public/icons/icon/tool-highlight.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M13.1024 25.123L26.6953 10.412C27.424 9.62334 27.3999 8.39987 26.6405 7.64054L23.3649 4.36488C22.6034 3.60337 21.3756 3.58158 20.5875 4.31559L5.98401 17.9175M13.1024 25.123L5.98401 17.9175M13.1024 25.123C12.6024 24.9563 11.3025 24.5374 10.1025 24.5374C8.90254 24.5374 7.60254 25.5374 7.10254 26.0374M5.98401 17.9175C6.15068 18.4175 6.60254 19.8374 6.60254 21.0374C6.60254 22.2374 5.60254 23.5374 5.10254 24.0374M7.10254 26.0374L6.10254 25.0374L5.10254 24.0374M7.10254 26.0374L6.10254 27.0374L2 27.1399L5.10254 24.0374" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M2 26L4 24L6 26L5 27L2 26Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/tool-laser.svg b/apps/web/public/icons/icon/tool-laser.svg
new file mode 100644
index 00000000..bf4d2d25
--- /dev/null
+++ b/apps/web/public/icons/icon/tool-laser.svg
@@ -0,0 +1,6 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M3.52185 26.4772L7.55602 22.443" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M3.52166 20.6267L5.88012 19.7974" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M10.2014 24.1187L9.37213 26.4772" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M27 7.36364L13.1112 21.2524C12.916 21.4477 12.5994 21.4477 12.4041 21.2524L8.7476 17.5959C8.55233 17.4006 8.55233 17.084 8.7476 16.8888L22.6364 3L27 7.36364Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/tool-line.svg b/apps/web/public/icons/icon/tool-line.svg
new file mode 100644
index 00000000..db6e280c
--- /dev/null
+++ b/apps/web/public/icons/icon/tool-line.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M25 4.99999L5 25" stroke="black" stroke-width="2" stroke-linecap="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/tool-media.svg b/apps/web/public/icons/icon/tool-media.svg
new file mode 100644
index 00000000..5d904155
--- /dev/null
+++ b/apps/web/public/icons/icon/tool-media.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M15 7.39844C13.0109 7.39844 11.3984 9.01091 11.3984 11C11.3984 12.9891 13.0109 14.6016 15 14.6016C16.9891 14.6016 18.6016 12.9891 18.6016 11C18.6016 9.01091 16.9891 7.39844 15 7.39844ZM13.3984 11C13.3984 10.1155 14.1155 9.39844 15 9.39844C15.8845 9.39844 16.6016 10.1155 16.6016 11C16.6016 11.8845 15.8845 12.6016 15 12.6016C14.1155 12.6016 13.3984 11.8845 13.3984 11Z" fill="black"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M4.5 2H25.5C26.8807 2 28 3.11929 28 4.5V25.5C28 26.8807 26.8807 28 25.5 28H4.5C3.11929 28 2 26.8807 2 25.5V4.5C2 3.11929 3.11929 2 4.5 2ZM26 4.5V18.5858L22.7071 15.2929C22.3166 14.9024 21.6834 14.9024 21.2929 15.2929L15.7496 20.8362L8.731 13.3176C8.54595 13.1194 8.28833 13.0048 8.01719 13.0001C7.74605 12.9955 7.48465 13.1011 7.29289 13.2929L4 16.5858V4.5C4 4.22386 4.22386 4 4.5 4H25.5C25.7761 4 26 4.22386 26 4.5ZM4 25.5V19.4142L7.97526 15.439L14.9886 22.9519L17.7474 26H4.5C4.22386 26 4 25.7761 4 25.5ZM25.5 26H20.4449L17.1047 22.3095L22 17.4142L26 21.4142V25.5C26 25.7761 25.7761 26 25.5 26Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/tool-note.svg b/apps/web/public/icons/icon/tool-note.svg
new file mode 100644
index 00000000..66b6d448
--- /dev/null
+++ b/apps/web/public/icons/icon/tool-note.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M17 27V19C17 17.8954 17.8954 17 19 17H27" stroke="black" stroke-width="2"/>
+<path d="M17.5789 26.45L26.3775 18.0914C26.775 17.7138 27 17.1896 27 16.6414V5C27 3.89543 26.1046 3 25 3H5C3.89543 3 3 3.89543 3 5V25C3 26.1046 3.89543 27 5 27H16.2014C16.7141 27 17.2072 26.8031 17.5789 26.45Z" stroke="black" stroke-width="2"/>
+</svg>
diff --git a/apps/web/public/icons/icon/tool-pencil.svg b/apps/web/public/icons/icon/tool-pencil.svg
new file mode 100644
index 00000000..dd50e219
--- /dev/null
+++ b/apps/web/public/icons/icon/tool-pencil.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M4.63636 23.1818L6.2348 19.4521C6.25985 19.3937 6.29584 19.3405 6.34082 19.2955L22.6364 3L27 7.36364L10.7045 23.6592C10.6595 23.7042 10.6063 23.7401 10.5479 23.7652L6.81818 25.3636M4.63636 23.1818L3 27L6.81818 25.3636M4.63636 23.1818L6.81818 25.3636" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M3 27L4.71429 23L7 25.2857L3 27Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/tool-pointer.svg b/apps/web/public/icons/icon/tool-pointer.svg
new file mode 100644
index 00000000..5c2ca688
--- /dev/null
+++ b/apps/web/public/icons/icon/tool-pointer.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M17.0715 28.7126L21.3174 26.7784L16.902 17.0768L24.9392 16.7223L7.12329 1.28723V24.8366L12.6484 19.011L17.0715 28.7126Z" stroke="black" stroke-width="2" stroke-linejoin="round"/>
+</svg>
diff --git a/apps/web/public/icons/icon/tool-screenshot.svg b/apps/web/public/icons/icon/tool-screenshot.svg
new file mode 100644
index 00000000..55cc5de7
--- /dev/null
+++ b/apps/web/public/icons/icon/tool-screenshot.svg
@@ -0,0 +1,5 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M20 15C20 17.7614 17.7614 20 15 20C12.2386 20 10 17.7614 10 15C10 12.2386 12.2386 10 15 10C17.7614 10 20 12.2386 20 15Z" stroke="black" stroke-width="2"/>
+<rect x="21" y="5" width="4" height="4" rx="2" fill="black"/>
+<path d="M25 3C26.1046 3 27 3.89543 27 5V25C27 26.1046 26.1046 27 25 27H5C3.89543 27 3 26.1046 3 25V5C3 3.89543 3.89543 3 5 3H25Z" stroke="black" stroke-width="2"/>
+</svg>
diff --git a/apps/web/public/icons/icon/tool-text.svg b/apps/web/public/icons/icon/tool-text.svg
new file mode 100644
index 00000000..b4b237bf
--- /dev/null
+++ b/apps/web/public/icons/icon/tool-text.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M11.5087 24.0001C10.9564 24.0001 10.5087 24.4478 10.5087 25.0001C10.5087 25.5524 10.9564 26.0001 11.5087 26.0001V24.0001ZM18.5087 26.0001C19.061 26.0001 19.5087 25.5524 19.5087 25.0001C19.5087 24.4478 19.061 24.0001 18.5087 24.0001V26.0001ZM7.00001 5.00006V4.00006C6.44773 4.00006 6.00001 4.44777 6.00001 5.00006L7.00001 5.00006ZM23 5.00006L24 5.00006C24 4.44777 23.5523 4.00006 23 4.00006V5.00006ZM22 8.99999C22 9.55227 22.4477 9.99998 23 9.99998C23.5523 9.99998 24 9.55226 24 8.99998L22 8.99999ZM6 8.99998C6 9.55226 6.44771 9.99998 7 9.99998C7.55228 9.99998 8 9.55227 8 8.99999L6 8.99998ZM16 24.5V5.50004H14V24.5H16ZM11.5087 26.0001H18.5087V24.0001H11.5087V26.0001ZM7.00001 6.00006H23V4.00006H7.00001V6.00006ZM24 8.99998L24 5.00006L22 5.00006L22 8.99999L24 8.99998ZM8 8.99999L8.00001 5.00006L6.00001 5.00006L6 8.99998L8 8.99999Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/trash.svg b/apps/web/public/icons/icon/trash.svg
new file mode 100644
index 00000000..8799f732
--- /dev/null
+++ b/apps/web/public/icons/icon/trash.svg
@@ -0,0 +1,5 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M10.5 3.3V5.1H19.5V3.3H10.5ZM21.3 5.1V2.85C21.3 2.10441 20.6956 1.5 19.95 1.5H10.05C9.30444 1.5 8.70002 2.10442 8.70002 2.85V5.1H3.30002C2.80297 5.1 2.40002 5.50294 2.40002 6C2.40002 6.49706 2.80297 6.9 3.30002 6.9H5.26103C5.2611 6.9509 5.26332 7.00224 5.26775 7.05395L6.96742 26.8539C7.04734 27.785 7.82639 28.5 8.76082 28.5H21.2392C22.1737 28.5 22.9527 27.785 23.0326 26.8539L24.7323 7.05395C24.7367 7.00224 24.7389 6.9509 24.739 6.9H26.7C27.1971 6.9 27.6 6.49706 27.6 6C27.6 5.50294 27.1971 5.1 26.7 5.1H21.3ZM22.9389 6.9H7.06116L8.76082 26.7H21.2392L22.9389 6.9Z" fill="black"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M11.3253 11.4031C11.8206 11.3618 12.2556 11.7299 12.2969 12.2253L13.1969 23.0253C13.2382 23.5206 12.8701 23.9556 12.3748 23.9969C11.8794 24.0382 11.4444 23.6701 11.4031 23.1747L10.5031 12.3747C10.4619 11.8794 10.8299 11.4444 11.3253 11.4031Z" fill="black"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M17.6253 23.9969C17.1299 23.9556 16.7619 23.5206 16.8031 23.0253L17.7031 12.2253C17.7444 11.7299 18.1794 11.3618 18.6748 11.4031C19.1701 11.4444 19.5382 11.8794 19.4969 12.3747L18.5969 23.1747C18.5556 23.6701 18.1206 24.0382 17.6253 23.9969Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/twitter.svg b/apps/web/public/icons/icon/twitter.svg
new file mode 100644
index 00000000..5e2dc0f0
--- /dev/null
+++ b/apps/web/public/icons/icon/twitter.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M20.72 4.13715C17.8194 4.13715 15.4668 6.48969 15.4668 9.39259C15.4668 9.8031 15.5142 10.2023 15.6022 10.588C11.2343 10.3681 7.36488 8.27609 4.77325 5.09801C4.32214 5.87618 4.06275 6.7784 4.06275 7.73927C4.06275 9.56176 4.99091 11.1711 6.39951 12.1128C5.53788 12.0846 4.72814 11.8489 4.0199 11.4553V11.523C4.0199 14.0684 5.82998 16.192 8.2344 16.6747C7.79231 16.7942 7.3288 16.8574 6.85062 16.8574C6.51228 16.8574 6.18185 16.8258 5.86156 16.7649C6.53033 18.8513 8.47011 20.3715 10.7696 20.4121C8.97084 21.8218 6.70513 22.662 4.24432 22.662C3.82028 22.662 3.40187 22.6372 2.99023 22.5887C5.31571 24.0808 8.07651 24.9492 11.0426 24.9492C20.7076 24.9492 25.9913 16.9442 25.9913 10.0016C25.9913 7.04254 23.8974 4.13715 20.72 4.13715Z" stroke="black" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M26.393 7.13758C27.4565 7.01014 28.4681 6.72819 29.4098 6.31091L29.4075 6.3143C28.7049 7.36651 27.814 8.29016 26.7877 9.03224L25.3667 5.50455C26.5633 5.26998 27.6877 4.8324 28.7027 4.23016C28.3102 5.45719 27.4779 6.48798 26.393 7.13758Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/undo.svg b/apps/web/public/icons/icon/undo.svg
new file mode 100644
index 00000000..cc94f8cd
--- /dev/null
+++ b/apps/web/public/icons/icon/undo.svg
@@ -0,0 +1,3 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M9.70711 4.29289C10.0976 4.68342 10.0976 5.31658 9.70711 5.70711L7.41421 8H18C22.9706 8 27 12.0294 27 17C27 21.9706 22.9706 26 18 26H10C9.44772 26 9 25.5523 9 25C9 24.4477 9.44772 24 10 24H18C21.866 24 25 20.866 25 17C25 13.134 21.866 10 18 10H7.41421L9.70711 12.2929C10.0976 12.6834 10.0976 13.3166 9.70711 13.7071C9.31658 14.0976 8.68342 14.0976 8.29289 13.7071L4.29289 9.70711C3.90237 9.31658 3.90237 8.68342 4.29289 8.29289L8.29289 4.29289C8.68342 3.90237 9.31658 3.90237 9.70711 4.29289Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/ungroup.svg b/apps/web/public/icons/icon/ungroup.svg
new file mode 100644
index 00000000..193da59e
--- /dev/null
+++ b/apps/web/public/icons/icon/ungroup.svg
@@ -0,0 +1,9 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M2.8999 1.8999C2.63468 1.8999 2.38033 2.00526 2.19279 2.1928C2.00526 2.38034 1.8999 2.63469 1.8999 2.89991L1.89993 6.89991C1.89994 7.45219 2.34766 7.89991 2.89994 7.8999C3.45223 7.8999 3.89994 7.45218 3.89993 6.89989L3.89991 3.8999H6.89993C7.45222 3.8999 7.89993 3.45219 7.89993 2.8999C7.89993 2.34762 7.45222 1.8999 6.89993 1.8999H2.8999Z" fill="black"/>
+<path d="M11.8999 1.8999C11.3476 1.8999 10.8999 2.34762 10.8999 2.8999C10.8999 3.45219 11.3476 3.8999 11.8999 3.8999H17.8999C18.4522 3.8999 18.8999 3.45219 18.8999 2.8999C18.8999 2.34762 18.4522 1.8999 17.8999 1.8999H11.8999Z" fill="black"/>
+<path d="M10.8999 26.8999C10.8999 26.3476 11.3476 25.8999 11.8999 25.8999H17.8999C18.4522 25.8999 18.8999 26.3476 18.8999 26.8999C18.8999 27.4522 18.4522 27.8999 17.8999 27.8999H11.8999C11.3476 27.8999 10.8999 27.4522 10.8999 26.8999Z" fill="black"/>
+<path d="M3.8999 11.8999C3.8999 11.3476 3.45219 10.8999 2.8999 10.8999C2.34762 10.8999 1.8999 11.3476 1.8999 11.8999V17.8999C1.8999 18.4522 2.34762 18.8999 2.8999 18.8999C3.45219 18.8999 3.8999 18.4522 3.8999 17.8999V11.8999Z" fill="black"/>
+<path d="M26.8999 10.8999C27.4522 10.8999 27.8999 11.3476 27.8999 11.8999V17.8999C27.8999 18.4522 27.4522 18.8999 26.8999 18.8999C26.3476 18.8999 25.8999 18.4522 25.8999 17.8999V11.8999C25.8999 11.3476 26.3476 10.8999 26.8999 10.8999Z" fill="black"/>
+<path d="M27.8999 22.8999C27.8999 22.3476 27.4522 21.8999 26.8999 21.8999C26.3476 21.8999 25.8999 22.3476 25.8999 22.8999L25.8982 25.8999H22.8999C22.3476 25.8999 21.8999 26.3476 21.8999 26.8999C21.8999 27.4522 22.3476 27.8999 22.8999 27.8999H26.8999C27.4522 27.8999 27.8999 27.4522 27.8999 26.8999L27.8999 22.8999Z" fill="black"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M27.2929 3.70709L3.70708 27.2929C3.31656 27.6834 2.68339 27.6834 2.29287 27.2929C1.90234 26.9024 1.90234 26.2692 2.29287 25.8787L25.8787 2.29288C26.2692 1.90236 26.9023 1.90236 27.2929 2.29288C27.6834 2.6834 27.6834 3.31657 27.2929 3.70709Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/unlock.svg b/apps/web/public/icons/icon/unlock.svg
new file mode 100644
index 00000000..b81dc0b4
--- /dev/null
+++ b/apps/web/public/icons/icon/unlock.svg
@@ -0,0 +1,4 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M5 15C5 14.4477 5.44772 14 6 14H24C24.5523 14 25 14.4477 25 15V27C25 27.5523 24.5523 28 24 28H6C5.44772 28 5 27.5523 5 27V15Z" stroke="black" stroke-width="2" stroke-linejoin="round"/>
+<path d="M21 14V8.27203C21 4.41155 18.8577 2 14.9972 2C12.8772 2 11.2772 2.69495 10.2653 3.99997C9.43436 5.07167 9 6.52554 9 8.2665" stroke="black" stroke-width="2"/>
+</svg>
diff --git a/apps/web/public/icons/icon/vertical-align-end.svg b/apps/web/public/icons/icon/vertical-align-end.svg
new file mode 100644
index 00000000..43c1af4e
--- /dev/null
+++ b/apps/web/public/icons/icon/vertical-align-end.svg
@@ -0,0 +1,5 @@
+<svg width="30" height="31" viewBox="0 0 30 31" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect x="26" y="19.7069" width="2" height="22" rx="1" transform="rotate(90 26 19.7069)" fill="black"/>
+<rect x="15" y="16.6169" width="2" height="8" rx="1" transform="rotate(-135 15 16.6169)" fill="black"/>
+<rect x="16.4142" y="15.2028" width="2" height="8" rx="1" transform="rotate(135 16.4142 15.2028)" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/vertical-align-middle.svg b/apps/web/public/icons/icon/vertical-align-middle.svg
new file mode 100644
index 00000000..7055700d
--- /dev/null
+++ b/apps/web/public/icons/icon/vertical-align-middle.svg
@@ -0,0 +1,7 @@
+<svg width="30" height="31" viewBox="0 0 30 31" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect x="15" y="19.2969" width="2" height="8" rx="1" transform="rotate(45 15 19.2969)" fill="black"/>
+<rect x="13.5858" y="20.7111" width="2" height="8" rx="1" transform="rotate(-45 13.5858 20.7111)" fill="black"/>
+<rect x="15" y="11.1179" width="2" height="8" rx="1" transform="rotate(-135 15 11.1179)" fill="black"/>
+<rect x="16.4142" y="9.70374" width="2" height="8" rx="1" transform="rotate(135 16.4142 9.70374)" fill="black"/>
+<rect x="4" y="16.2069" width="2" height="22" rx="1" transform="rotate(-90 4 16.2069)" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/vertical-align-start.svg b/apps/web/public/icons/icon/vertical-align-start.svg
new file mode 100644
index 00000000..a3656224
--- /dev/null
+++ b/apps/web/public/icons/icon/vertical-align-start.svg
@@ -0,0 +1,5 @@
+<svg width="30" height="31" viewBox="0 0 30 31" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect x="4" y="10.7069" width="2" height="22" rx="1" transform="rotate(-90 4 10.7069)" fill="black"/>
+<rect x="15" y="13.7986" width="2" height="8" rx="1" transform="rotate(45 15 13.7986)" fill="black"/>
+<rect x="13.5858" y="15.2128" width="2" height="8" rx="1" transform="rotate(-45 13.5858 15.2128)" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/warning-triangle.svg b/apps/web/public/icons/icon/warning-triangle.svg
new file mode 100644
index 00000000..6082a66a
--- /dev/null
+++ b/apps/web/public/icons/icon/warning-triangle.svg
@@ -0,0 +1,5 @@
+<svg width="32" height="30" viewBox="0 0 32 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="16.3997" cy="19.4395" r="1.5" fill="black"/>
+<path d="M15.0538 10.4595C15.0244 9.69574 15.6356 9.06055 16.3999 9.06055C17.1643 9.06055 17.7755 9.69574 17.7461 10.4595L17.4384 15.4613C17.4169 16.0193 16.9583 16.4606 16.3999 16.4606C15.8415 16.4606 15.383 16.0193 15.3615 15.4613L15.0538 10.4595Z" fill="black"/>
+<path d="M29.9592 23.8499L16.5153 1.29299C16.2826 0.902566 15.7171 0.902566 15.4845 1.29299L2.04056 23.8499C1.80219 24.2498 2.09037 24.7571 2.55596 24.7571H29.4438C29.9094 24.7571 30.1976 24.2498 29.9592 23.8499Z" stroke="black" stroke-width="2"/>
+</svg>
diff --git a/apps/web/public/icons/icon/zoom-in.svg b/apps/web/public/icons/icon/zoom-in.svg
new file mode 100644
index 00000000..bbc79563
--- /dev/null
+++ b/apps/web/public/icons/icon/zoom-in.svg
@@ -0,0 +1,6 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="12.4077" cy="12.4077" r="9.40773" stroke="black" stroke-width="2"/>
+<rect x="11.1066" y="7.85431" width="2.60193" height="9.10677" rx="1.30097" fill="black"/>
+<rect x="16.9609" y="11.1067" width="2.60193" height="9.10677" rx="1.30097" transform="rotate(90 16.9609 11.1067)" fill="black"/>
+<path d="M27.0801 25.2404C27.5881 25.7484 27.5881 26.5721 27.0801 27.0802C26.572 27.5883 25.7483 27.5883 25.2402 27.0802L18.8888 20.7288C18.3861 20.2261 18.38 19.413 18.8752 18.9028C19.3809 18.3817 20.2152 18.3755 20.7287 18.8889L27.0801 25.2404Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/icons/icon/zoom-out.svg b/apps/web/public/icons/icon/zoom-out.svg
new file mode 100644
index 00000000..9b8d80c1
--- /dev/null
+++ b/apps/web/public/icons/icon/zoom-out.svg
@@ -0,0 +1,5 @@
+<svg width="30" height="30" viewBox="0 0 30 30" fill="none" xmlns="http://www.w3.org/2000/svg">
+<circle cx="12.4077" cy="12.4077" r="9.40773" stroke="black" stroke-width="2"/>
+<rect x="16.961" y="11.1067" width="2.60193" height="9.10677" rx="1.30097" transform="rotate(90 16.961 11.1067)" fill="black"/>
+<path d="M27.0801 25.2404C27.5881 25.7484 27.5881 26.5721 27.0801 27.0802C26.572 27.5883 25.7483 27.5883 25.2402 27.0802L18.8888 20.7288C18.3861 20.2261 18.38 19.413 18.8752 18.9028C19.3809 18.3817 20.2152 18.3755 20.7287 18.8889L27.0801 25.2404Z" fill="black"/>
+</svg>
diff --git a/apps/web/public/landing-ui.svg b/apps/web/public/landing-ui.svg
index 43e64a70..2d398618 100644
--- a/apps/web/public/landing-ui.svg
+++ b/apps/web/public/landing-ui.svg
@@ -1,5 +1,5 @@
<svg width="1364" height="804" viewBox="0 0 1364 804" fill="none" xmlns="http://www.w3.org/2000/svg">
-<rect x="1.39184" y="1.39184" width="1361.22" height="800.306" rx="32.0122" fill="#171B1F"/>
+<rect x="1.39184" y="1.39184" width="1361.22" height="800.306" rx="32.0122" fill="#151515"/>
<rect x="1.39184" y="1.39184" width="1361.22" height="800.306" rx="32.0122" stroke="url(#paint0_linear_380_370)" stroke-width="2.78367"/>
<g clip-path="url(#clip0_380_370)">
<rect x="377.086" y="266.692" width="239.061" height="81.1905" rx="10.8254" fill="#1F2428"/>
@@ -54,7 +54,7 @@
<stop offset="1" stop-color="#0F1114"/>
</linearGradient>
<linearGradient id="paint1_linear_380_370" x1="978.794" y1="307.287" x2="936.395" y2="307.287" gradientUnits="userSpaceOnUse">
-<stop stop-color="#171B1F"/>
+<stop stop-color="#151515"/>
<stop offset="1" stop-color="#1F2428" stop-opacity="0"/>
</linearGradient>
<linearGradient id="paint2_linear_380_370" x1="762.744" y1="670.298" x2="768.028" y2="776.787" gradientUnits="userSpaceOnUse">
diff --git a/apps/web/public/translations/ar.json b/apps/web/public/translations/ar.json
new file mode 100644
index 00000000..5781e4dd
--- /dev/null
+++ b/apps/web/public/translations/ar.json
@@ -0,0 +1,295 @@
+{
+ "action.align-bottom": "محاذاة للأسفل",
+ "action.align-center-horizontal": "محاذاة أفقيًا",
+ "action.align-center-horizontal.short": "محاذاة أفقية",
+ "action.align-center-vertical": "محاذاة عموديًا",
+ "action.align-center-vertical.short": "محاذاة عمودية",
+ "action.align-left": "محاذاة لليسار",
+ "action.align-right": "محاذاة لليمين",
+ "action.align-top": "محاذاة للأعلى",
+ "action.back-to-content": "العودة إلى المحتوى",
+ "action.bring-forward": "إحضار إلى الأمام",
+ "action.bring-to-front": "إحضار إلى المقدمة",
+ "action.convert-to-bookmark": "تحويل إلى إشارة مرجعية",
+ "action.convert-to-embed": "تحويل إلى تضمين",
+ "action.copy": "نسخ",
+ "action.copy-as-json": "نسخ بصيغة JSON",
+ "action.copy-as-json.short": "صيغة JSON",
+ "action.copy-as-png": "نسخ بصيغة PNG",
+ "action.copy-as-png.short": "صيغة PNG",
+ "action.copy-as-svg": "نسخ بصيغة SVG",
+ "action.copy-as-svg.short": "صيغة SVG",
+ "action.cut": "قص",
+ "action.delete": "حذف",
+ "action.distribute-horizontal": "توزيع أفقيًا",
+ "action.distribute-horizontal.short": "توزيع أفقي",
+ "action.distribute-vertical": "توزيع عموديًا",
+ "action.distribute-vertical.short": "توزيع عمودي",
+ "action.duplicate": "نسخة مكررة",
+ "action.edit-link": "تعديل الرابط",
+ "action.exit-pen-mode": "الخروج من وضع القلم",
+ "action.export-as-json": "تصدير بصيغة JSON",
+ "action.export-as-json.short": "صيغة JSON",
+ "action.export-as-png": "تصدير بصيغة PNG",
+ "action.export-as-png.short": "صيغة PNG",
+ "action.export-as-svg": "تصدير بصيغة SVG",
+ "action.export-as-svg.short": "صيغة SVG",
+ "action.flip-horizontal": "عكس أفقيًا",
+ "action.flip-horizontal.short": "عكس أفقي",
+ "action.flip-vertical": "عكس عموديًا",
+ "action.flip-vertical.short": "عكس عمودي",
+ "action.group": "مجموعة",
+ "action.insert-media": "تحميل الوسائط",
+ "action.new-shared-project": "مشروع جديد تمت مشاركته",
+ "action.open-embed-link": "فتح الرابط",
+ "action.open-file": "فتح ملف",
+ "action.pack": "حزمة",
+ "action.paste": "لصق",
+ "action.print": "طباعة",
+ "action.redo": "إعادة",
+ "action.rotate-ccw": "تدوير عكس اتجاه عقارب الساعة",
+ "action.rotate-cw": "تدوير في اتجاه عقارب الساعة",
+ "action.save-copy": "حفظ نسخة",
+ "action.select-all": "تحديد الكل",
+ "action.select-none": "عدم تحديد شيء",
+ "action.send-backward": "إرسال إلى الخلف",
+ "action.send-to-back": "إرسال إلى خلف",
+ "action.share-project": "شارك هذا المشروع",
+ "action.stack-horizontal": "تكديس أفقيًا",
+ "action.stack-horizontal.short": "تكديس أفقي",
+ "action.stack-vertical": "تكديس عموديًا",
+ "action.stack-vertical.short": "تكديس عمودي",
+ "action.stretch-horizontal": "تمدد أفقيًا",
+ "action.stretch-horizontal.short": "تمدد أفقي",
+ "action.stretch-vertical": "تمدد عموديًا",
+ "action.stretch-vertical.short": "تمدد عمودي",
+ "action.toggle-auto-size": "التبديل للحجم التلقائي",
+ "action.toggle-dark-mode": "التبديل للوضع المظلم",
+ "action.toggle-dark-mode.menu": "الوضع المظلم",
+ "action.toggle-debug-mode": "التبديل لوضع التصحيح",
+ "action.toggle-debug-mode.menu": "وضع التصحيح",
+ "action.toggle-focus-mode": "التبديل لوضع التركيز",
+ "action.toggle-focus-mode.menu": "وضع التركيز",
+ "action.toggle-grid": "التبديل لوضع الشبكة",
+ "action.toggle-grid.menu": "إظهار الشبكة",
+ "action.toggle-snap-mode": "التبديل لوضع المحاذاة الدائمة",
+ "action.toggle-snap-mode.menu": "وضع المحاذاة الدائمة",
+ "action.toggle-tool-lock": "تبديل قفل الأداة",
+ "action.toggle-tool-lock.menu": "قفل الأداة",
+ "action.toggle-transparent": "تبديل الخلفية الشفافة",
+ "action.toggle-transparent.context-menu": "شفاف",
+ "action.toggle-transparent.menu": "شفاف",
+ "action.undo": "تراجع",
+ "action.ungroup": "فك التجميع",
+ "action.zoom-in": "تكبير",
+ "action.zoom-out": "تصغير",
+ "action.zoom-to-100": "التكبير إلى 100%",
+ "action.zoom-to-fit": "التكبير للملاءمة",
+ "action.zoom-to-selection": "التكبير للتحديد",
+ "actions-menu.title": "الإجراءات",
+ "align-style.end": "النهاية",
+ "align-style.justify": "ضبط",
+ "align-style.middle": "الوسط",
+ "align-style.start": "البداية",
+ "arrowheadEnd-style.arrow": "سهم",
+ "arrowheadEnd-style.bar": "شريط",
+ "arrowheadEnd-style.diamond": "شكل الماسة",
+ "arrowheadEnd-style.dot": "نقطة",
+ "arrowheadEnd-style.inverted": "معكوس",
+ "arrowheadEnd-style.none": "لا يوجد",
+ "arrowheadEnd-style.pipe": "أنبوب",
+ "arrowheadEnd-style.square": "مربع",
+ "arrowheadEnd-style.triangle": "مثلث",
+ "arrowheadStart-style.arrow": "سهم",
+ "arrowheadStart-style.bar": "شريط",
+ "arrowheadStart-style.diamond": "شكل الماسة",
+ "arrowheadStart-style.dot": "نقطة",
+ "arrowheadStart-style.inverted": "معكوس",
+ "arrowheadStart-style.none": "لا يوجد",
+ "arrowheadStart-style.pipe": "أنبوب",
+ "arrowheadStart-style.square": "مربع",
+ "arrowheadStart-style.triangle": "مثلث",
+ "color-style.black": "أسود",
+ "color-style.blue": "أزرق",
+ "color-style.green": "أخضر",
+ "color-style.grey": "رمادي",
+ "color-style.light-blue": "أزرق فاتح",
+ "color-style.light-green": "أخضر فاتح",
+ "color-style.light-red": "أحمر فاتح",
+ "color-style.light-violet": "بنفسجي فاتح",
+ "color-style.orange": "برتقالي",
+ "color-style.red": "أحمر",
+ "color-style.violet": "بنفسجي",
+ "color-style.yellow": "أصفر",
+ "context-menu.arrange": "ترتيب",
+ "context-menu.copy-as": "النسخ بصيغة",
+ "context-menu.export-as": "التصدير بصيغة",
+ "context-menu.move-to-page": "انتقل إلى صفحة",
+ "context-menu.reorder": "إعادة الترتيب",
+ "context.pages.new-page": "صفحة جديدة",
+ "dash-style.dashed": "بشرطة",
+ "dash-style.dotted": "منقط",
+ "dash-style.draw": "رسم",
+ "dash-style.solid": "مليء",
+ "edit-link-dialog.cancel": "إلغاء",
+ "edit-link-dialog.clear": "مسح",
+ "edit-link-dialog.detail": "سيتم فتح الروابط في علامة تبويب جديدة.",
+ "edit-link-dialog.invalid-url": "يجب أن يكون الرابط عنوان URL صالحًا.",
+ "edit-link-dialog.save": "المتابعة",
+ "edit-link-dialog.title": "تعديل الرابط",
+ "edit-link-dialog.url": "عنوان URL",
+ "edit-pages-dialog.move-down": "الانتقال لأسفل",
+ "edit-pages-dialog.move-up": "الانتقال لأعلى",
+ "embed-dialog.back": "العودة",
+ "embed-dialog.cancel": "إلغاء",
+ "embed-dialog.create": "إنشاء",
+ "embed-dialog.instruction": "الصق عنوان URL الخاص بالموقع لإنشاء التضمين.",
+ "embed-dialog.invalid-url": "لم نتمكن من إنشاء تضمين من عنوان URL هذا.",
+ "embed-dialog.title": "إنشاء تضمين",
+ "embed-dialog.url": "عنوان URL",
+ "file-system.confirm-open.cancel": "إلغاء",
+ "file-system.confirm-open.description": "سيؤدي فتح ملف إلى استبدال مشروعك الحالي وستفقد أي تغييرات غير محفوظة. هل أنت متأكد من أنك تريد المتابعة؟",
+ "file-system.confirm-open.dont-show-again": "لا تسأل مرة أخرى",
+ "file-system.confirm-open.open": "فتح الملف",
+ "file-system.confirm-open.title": "هل تريد استبدال المشروع الحالي؟",
+ "file-system.file-open-error.file-format-version-too-new": "الملف الذي حاولت فتحه تابع لإصدار أحدث من tldraw. يُرجى إعادة تحميل الصفحة والمحاولة مرة أخرى.",
+ "file-system.file-open-error.generic-corrupted-file": "الملف الذي حاولت فتحه تالف.",
+ "file-system.file-open-error.not-a-tldraw-file": "لا يبدو أن الملف الذي حاولت فتحه هو ملف tldraw.",
+ "file-system.file-open-error.title": "تعذر فتح الملف",
+ "file-system.shared-document-file-open-error.description": "فتح الملفات من المشاريع المشتركة غير مدعوم.",
+ "file-system.shared-document-file-open-error.title": "تعذر فتح الملف",
+ "fill-style.none": "لا يوجد",
+ "fill-style.pattern": "نمط",
+ "fill-style.semi": "شبه",
+ "fill-style.solid": "مليء",
+ "focus-mode.toggle-focus-mode": "تبديل وضع التركيز",
+ "font-style.draw": "رسم",
+ "font-style.mono": "أحادي",
+ "font-style.sans": "غير مذيَّل",
+ "font-style.serif": "خط مذيَّل",
+ "geo-style.arrow-down": "سهم للأسفل",
+ "geo-style.arrow-left": "سهم لليسار",
+ "geo-style.arrow-right": "سهم لليمين",
+ "geo-style.arrow-up": "سهم للأعلى",
+ "geo-style.diamond": "شكل الماسة",
+ "geo-style.ellipse": "شكل بيضاوي",
+ "geo-style.hexagon": "شكل سداسي",
+ "geo-style.octagon": "شكل ثماني",
+ "geo-style.oval": "شكل بيضاوي",
+ "geo-style.pentagon": "شكل خماسي",
+ "geo-style.rectangle": "مستطيل",
+ "geo-style.rhombus": "معيَّن",
+ "geo-style.rhombus-2": "المعيَّن 2",
+ "geo-style.star": "نجمة",
+ "geo-style.trapezoid": "شبه منحرف",
+ "geo-style.triangle": "مثلث",
+ "geo-style.x-box": "مربع X",
+ "help-menu.about": "حول",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "اختصارات لوحة المفاتيح",
+ "help-menu.title": "المساعدة والمصادر",
+ "help-menu.twitter": "Twitter",
+ "menu.copy-as": "النسخ بصيغة",
+ "menu.edit": "تعديل",
+ "menu.export-as": "التصدير بصيغة",
+ "menu.file": "ملف",
+ "menu.language": "اللغة",
+ "menu.preferences": "التفضيلات",
+ "menu.title": "القائمة",
+ "menu.view": "عرض",
+ "navigation-zone.toggle-minimap": "تبديل الخريطة المصغرة",
+ "navigation-zone.zoom": "تكبير",
+ "opacity-style.0.1": "10%",
+ "opacity-style.0.25": "25%",
+ "opacity-style.0.5": "50%",
+ "opacity-style.0.75": "75%",
+ "opacity-style.1": "100%",
+ "page-menu.create-new-page": "إنشاء صفحة جديدة",
+ "page-menu.edit-done": "تم",
+ "page-menu.edit-start": "تعديل",
+ "page-menu.max-page-count-reached": "تم الوصول إلى الحد الأقصى لعدد الصفحات",
+ "page-menu.new-page-initial-name": "الصفحة 1",
+ "page-menu.submenu.delete": "حذف",
+ "page-menu.submenu.duplicate-page": "نسخة مكررة",
+ "page-menu.submenu.move-down": "الانتقال لأسفل",
+ "page-menu.submenu.move-up": "الانتقال لأعلى",
+ "page-menu.submenu.rename": "إعادة تسمية",
+ "page-menu.submenu.title": "القائمة",
+ "page-menu.title": "الصفحات",
+ "people-menu.change-color": "تغيير اللون",
+ "people-menu.change-name": "تغيير الاسم",
+ "people-menu.invite": "دعوة الآخرين",
+ "people-menu.title": "الأشخاص",
+ "people-menu.user": "(أنت)",
+ "share-menu.copy-link": "نسخ الرابط",
+ "share-menu.copy-link-note": "سيتمكن أي شخص لديه الرابط من عرض هذا المشروع وتعديله.",
+ "share-menu.copy-readonly-link": "نسخ رابط للقراءة فقط",
+ "share-menu.copy-readonly-link-note": "سيتمكن أي شخص لديه الرابط من عرض هذا المشروع (ولكن لن يتمكن من تعديله).",
+ "share-menu.offline-note": "ستؤدي مشاركة هذا المشروع إلى إنشاء نسخة مستضافة مباشرة على عنوان URL جديد. كما يمكنك مشاركة عنوان URL مع ما يصل إلى ثلاثين شخصًا آخر لعرض المشروع وتعديله معًا.",
+ "share-menu.project-too-large": "عذرًا، لا يمكن مشاركة هذا المشروع لأنه كبير جدًا. نحن نعمل على ذلك!",
+ "share-menu.readonly-link": "للقراءة فقط",
+ "share-menu.share-project": "مشاركة هذا المشروع",
+ "share-menu.title": "مشاركة",
+ "shortcuts-dialog.edit": "تعديل",
+ "shortcuts-dialog.file": "ملف",
+ "shortcuts-dialog.preferences": "التفضيلات",
+ "shortcuts-dialog.title": "اختصارات لوحة المفاتيح",
+ "shortcuts-dialog.tools": "الأدوات",
+ "shortcuts-dialog.transform": "تحويل",
+ "shortcuts-dialog.view": "عرض",
+ "size-style.l": "كبير",
+ "size-style.m": "متوسط",
+ "size-style.s": "صغير",
+ "size-style.xl": "كبير جدًا",
+ "spline-style.cubic": "مكعب",
+ "spline-style.line": "خط",
+ "style-panel.align": "محاذاة",
+ "style-panel.arrowheads": "رؤوس الأسهم",
+ "style-panel.color": "اللون",
+ "style-panel.dash": "شرطة",
+ "style-panel.fill": "ملء",
+ "style-panel.font": "الخط",
+ "style-panel.geo": "الشكل",
+ "style-panel.mixed": "مختلط",
+ "style-panel.opacity": "معدل الشفافية",
+ "style-panel.size": "الحجم",
+ "style-panel.spline": "Spline",
+ "style-panel.title": "الأنماط",
+ "toast.close": "إغلاق",
+ "toast.error.copy-fail.desc": "فشل في نسخ الصورة",
+ "toast.error.copy-fail.title": "فشل النسخ",
+ "toast.error.export-fail.desc": "فشل في تصدير الصورة",
+ "toast.error.export-fail.title": "فشل التصدير",
+ "tool-panel.drawing": "الرسم",
+ "tool-panel.shapes": "الأشكال",
+ "tool.arrow": "سهم",
+ "tool.arrow-down": "سهم للأسفل",
+ "tool.arrow-left": "سهم لليسار",
+ "tool.arrow-right": "سهم لليمين",
+ "tool.arrow-up": "سهم للأعلى",
+ "tool.asset": "الأصل",
+ "tool.diamond": "شكل الماسة",
+ "tool.draw": "رسم",
+ "tool.ellipse": "شكل بيضاوي",
+ "tool.embed": "تضمين",
+ "tool.eraser": "ممحاة",
+ "tool.frame": "إطار",
+ "tool.hand": "أيقونة يد",
+ "tool.hexagon": "شكل سداسي",
+ "tool.line": "خط",
+ "tool.note": "ملحوظة",
+ "tool.octagon": "شكل ثماني",
+ "tool.oval": "شكل بيضاوي",
+ "tool.pentagon": "شكل خماسي",
+ "tool.rectangle": "مستطيل",
+ "tool.rhombus": "معين",
+ "tool.select": "تحديد",
+ "tool.star": "نجمة",
+ "tool.text": "النص",
+ "tool.trapezoid": "شبه منحرف",
+ "tool.triangle": "مثلث",
+ "tool.x-box": "X box",
+ "vscode.file-open.desc": "تم إنشاء هذا الملف باستخدام إصدار سابق من tldraw. هل ترغب في تحديث الملف للعمل مع الإصدار الجديد؟",
+ "vscode.file-open.dont-show-again": "لا تسأل مرةً أخرى"
+}
diff --git a/apps/web/public/translations/ca.json b/apps/web/public/translations/ca.json
new file mode 100644
index 00000000..3dabf6cf
--- /dev/null
+++ b/apps/web/public/translations/ca.json
@@ -0,0 +1,295 @@
+{
+ "action.align-bottom": "Alinear amb la part inferior",
+ "action.align-center-horizontal": "Alinear horitzontalment",
+ "action.align-center-horizontal.short": "Alinear H",
+ "action.align-center-vertical": "Alinear verticalment",
+ "action.align-center-vertical.short": "Alinear V",
+ "action.align-left": "Alinear a l'esquerra",
+ "action.align-right": "Alinear a la dreta",
+ "action.align-top": "Alinear amb la part superior",
+ "action.back-to-content": "Tornar al contingut",
+ "action.bring-forward": "Portar endavant",
+ "action.bring-to-front": "Portar al davant",
+ "action.convert-to-bookmark": "Convertir a marcador",
+ "action.convert-to-embed": "Convertir a inserció",
+ "action.copy": "Copiar",
+ "action.copy-as-json": "Copiar com a JSON",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "Copiar com a PNG",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "Copiar com a SVG",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "Tallar",
+ "action.delete": "Esborrar",
+ "action.distribute-horizontal": "Distribuir horitzontalment",
+ "action.distribute-horizontal.short": "Distribuir H",
+ "action.distribute-vertical": "Distribuir verticalment",
+ "action.distribute-vertical.short": "Distribuir V",
+ "action.duplicate": "Duplicar",
+ "action.edit-link": "Editar enllaç",
+ "action.exit-pen-mode": "Sortir del mode bolígraf",
+ "action.export-as-json": "Exportar com a JSON",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "Exportar com a PNG",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "Exportar com a SVG",
+ "action.export-as-svg.short": "SVG",
+ "action.flip-horizontal": "Girar horitzontalment",
+ "action.flip-horizontal.short": "Girar H",
+ "action.flip-vertical": "Girar verticalment",
+ "action.flip-vertical.short": "Girar V",
+ "action.group": "Grup",
+ "action.insert-media": "Pujar contingut multimèdia",
+ "action.new-shared-project": "Nou projecte compartit",
+ "action.open-embed-link": "Obrir enllaç",
+ "action.open-file": "Obrir fitxer",
+ "action.pack": "Empaquetar",
+ "action.paste": "Enganxar",
+ "action.print": "Imprimir",
+ "action.redo": "Refer",
+ "action.rotate-ccw": "Girar en sentit antihorari",
+ "action.rotate-cw": "Girar en sentit horari",
+ "action.save-copy": "Desar una còpia",
+ "action.select-all": "Seleccionar tot",
+ "action.select-none": "Desseleccionar tot",
+ "action.send-backward": "Enviar cap enrere",
+ "action.send-to-back": "Enviar al fons",
+ "action.share-project": "Compartir aquest projecte",
+ "action.stack-horizontal": "Apilar horitzontalment",
+ "action.stack-horizontal.short": "Apilar H",
+ "action.stack-vertical": "Apilar verticalment",
+ "action.stack-vertical.short": "Apilar V",
+ "action.stretch-horizontal": "Estirar horitzontalment",
+ "action.stretch-horizontal.short": "Estirament H",
+ "action.stretch-vertical": "Estirar verticalment",
+ "action.stretch-vertical.short": "Estirament V",
+ "action.toggle-auto-size": "Commutar grandària automàtica",
+ "action.toggle-dark-mode": "Commutar mode fosc",
+ "action.toggle-dark-mode.menu": "Mode fosc",
+ "action.toggle-debug-mode": "Commutar mode de depuració",
+ "action.toggle-debug-mode.menu": "Mode de depuració",
+ "action.toggle-focus-mode": "Commutar mode de concentració",
+ "action.toggle-focus-mode.menu": "Mode de concentració",
+ "action.toggle-grid": "Commutar quadrícula",
+ "action.toggle-grid.menu": "Mostra la quadrícula",
+ "action.toggle-snap-mode": "Commuta acoblament permanent",
+ "action.toggle-snap-mode.menu": "Acoblament permanent",
+ "action.toggle-tool-lock": "Commutar el bloqueig d'eines",
+ "action.toggle-tool-lock.menu": "Bloqueig d'eines",
+ "action.toggle-transparent": "Commutar el fons transparent",
+ "action.toggle-transparent.context-menu": "Transparent",
+ "action.toggle-transparent.menu": "Transparent",
+ "action.undo": "Desfer",
+ "action.ungroup": "Desagrupar",
+ "action.zoom-in": "Apropar-se",
+ "action.zoom-out": "Allunyar-se",
+ "action.zoom-to-100": "Amplia fins al 100%",
+ "action.zoom-to-fit": "Enquadrar",
+ "action.zoom-to-selection": "Ampliar fins a la selecció",
+ "actions-menu.title": "Accions",
+ "align-style.end": "Fi",
+ "align-style.justify": "Justificar",
+ "align-style.middle": "Mig",
+ "align-style.start": "Inici",
+ "arrowheadEnd-style.arrow": "Fletxa",
+ "arrowheadEnd-style.bar": "Barra",
+ "arrowheadEnd-style.diamond": "Diamant",
+ "arrowheadEnd-style.dot": "Punt",
+ "arrowheadEnd-style.inverted": "Invertit",
+ "arrowheadEnd-style.none": "Cap",
+ "arrowheadEnd-style.pipe": "Tub",
+ "arrowheadEnd-style.square": "Quadrat",
+ "arrowheadEnd-style.triangle": "Triangle",
+ "arrowheadStart-style.arrow": "Fletxa",
+ "arrowheadStart-style.bar": "Barra",
+ "arrowheadStart-style.diamond": "Diamant",
+ "arrowheadStart-style.dot": "Punt",
+ "arrowheadStart-style.inverted": "Invertit",
+ "arrowheadStart-style.none": "Cap",
+ "arrowheadStart-style.pipe": "Tub",
+ "arrowheadStart-style.square": "Quadrat",
+ "arrowheadStart-style.triangle": "Triangle",
+ "color-style.black": "Negre",
+ "color-style.blue": "Blau",
+ "color-style.green": "Verd",
+ "color-style.grey": "Gris",
+ "color-style.light-blue": "Blau clar",
+ "color-style.light-green": "Verd clar",
+ "color-style.light-red": "Vermell clar",
+ "color-style.light-violet": "Violeta clar",
+ "color-style.orange": "Taronja",
+ "color-style.red": "Vermell",
+ "color-style.violet": "Violeta",
+ "color-style.yellow": "Groc",
+ "context-menu.arrange": "Organitzar",
+ "context-menu.copy-as": "Copiar com a...",
+ "context-menu.export-as": "Exportar com a...",
+ "context-menu.move-to-page": "Moure a la pàgina",
+ "context-menu.reorder": "Reordenar",
+ "context.pages.new-page": "Nova pàgina",
+ "dash-style.dashed": "Guions",
+ "dash-style.dotted": "Puntejat",
+ "dash-style.draw": "Dibuixar",
+ "dash-style.solid": "Sòlid",
+ "edit-link-dialog.cancel": "Cancel·lar",
+ "edit-link-dialog.clear": "Esborrar tot",
+ "edit-link-dialog.detail": "Els enllaços s'obriran en una pestanya nova.",
+ "edit-link-dialog.invalid-url": "Un enllaç ha de ser un URL vàlid.",
+ "edit-link-dialog.save": "Continuar",
+ "edit-link-dialog.title": "Editar l'enllaç",
+ "edit-link-dialog.url": "URL",
+ "edit-pages-dialog.move-down": "Baixar",
+ "edit-pages-dialog.move-up": "Pujar",
+ "embed-dialog.back": "Enrere",
+ "embed-dialog.cancel": "Cancel·lar",
+ "embed-dialog.create": "Crear",
+ "embed-dialog.instruction": "Enganxeu l'URL del lloc per crear la inserció.",
+ "embed-dialog.invalid-url": "No hem pogut crear una inserció a partir d'aquest URL.",
+ "embed-dialog.title": "Crear inserció",
+ "embed-dialog.url": "URL",
+ "file-system.confirm-open.cancel": "Cancel·lar",
+ "file-system.confirm-open.description": "L'obertura d'un fitxer substituirà el vostre projecte actual i es perdran els canvis no desats. Voleu continuar?",
+ "file-system.confirm-open.dont-show-again": "No preguntar-ho més",
+ "file-system.confirm-open.open": "Obrir fitxer",
+ "file-system.confirm-open.title": "Vols sobreescriure el projecte actual?",
+ "file-system.file-open-error.file-format-version-too-new": "El fitxer que heu intentat obrir és d'una versió més recent de tldraw. Torneu a carregar la pàgina i torneu-ho a provar.",
+ "file-system.file-open-error.generic-corrupted-file": "El fitxer que heu intentat obrir està malmès.",
+ "file-system.file-open-error.not-a-tldraw-file": "El fitxer que heu intentat obrir no és un fitxer tldraw.",
+ "file-system.file-open-error.title": "No s'ha pogut obrir el fitxer",
+ "file-system.shared-document-file-open-error.description": "No es poden obrir fitxers de projectes compartits.",
+ "file-system.shared-document-file-open-error.title": "No s'ha pogut obrir el fitxer",
+ "fill-style.none": "Cap",
+ "fill-style.pattern": "Patró",
+ "fill-style.semi": "Semi",
+ "fill-style.solid": "Sòlid",
+ "focus-mode.toggle-focus-mode": "Commutar mode de concentració",
+ "font-style.draw": "Dibuixar",
+ "font-style.mono": "Mono",
+ "font-style.sans": "Sans",
+ "font-style.serif": "Serif",
+ "geo-style.arrow-down": "Fletxa cap avall",
+ "geo-style.arrow-left": "Fletxa cap a l'esquerra",
+ "geo-style.arrow-right": "Fletxa cap a la dreta",
+ "geo-style.arrow-up": "Fletxa cap amunt",
+ "geo-style.diamond": "Diamant",
+ "geo-style.ellipse": "El·lipse",
+ "geo-style.hexagon": "Hexàgon",
+ "geo-style.octagon": "Octàgon",
+ "geo-style.oval": "Oval",
+ "geo-style.pentagon": "Pentàgon",
+ "geo-style.rectangle": "Rectangle",
+ "geo-style.rhombus": "Rombe",
+ "geo-style.rhombus-2": "Rombe 2",
+ "geo-style.star": "Estrella",
+ "geo-style.trapezoid": "Trapezoide",
+ "geo-style.triangle": "Triangle",
+ "geo-style.x-box": "Caixa X",
+ "help-menu.about": "Sobre nosaltres",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "Dreceres de teclat",
+ "help-menu.title": "Ajuda i recursos",
+ "help-menu.twitter": "Twitter",
+ "menu.copy-as": "Copiar com a...",
+ "menu.edit": "Editar",
+ "menu.export-as": "Exportar com a...",
+ "menu.file": "Fitxer",
+ "menu.language": "Llengua",
+ "menu.preferences": "Preferències",
+ "menu.title": "Menú",
+ "menu.view": "Visualització",
+ "navigation-zone.toggle-minimap": "Commutar minimapa",
+ "navigation-zone.zoom": "Zoom",
+ "opacity-style.0.1": "10%",
+ "opacity-style.0.25": "25%",
+ "opacity-style.0.5": "50%",
+ "opacity-style.0.75": "75%",
+ "opacity-style.1": "100%",
+ "page-menu.create-new-page": "Crear una pàgina nova",
+ "page-menu.edit-done": "Fet",
+ "page-menu.edit-start": "Editar",
+ "page-menu.max-page-count-reached": "S'han arribat al màxim de pàgines",
+ "page-menu.new-page-initial-name": "Pàgina 1",
+ "page-menu.submenu.delete": "Esborrar",
+ "page-menu.submenu.duplicate-page": "Duplicar",
+ "page-menu.submenu.move-down": "Baixar",
+ "page-menu.submenu.move-up": "Pujar",
+ "page-menu.submenu.rename": "Rebatejar",
+ "page-menu.submenu.title": "Menú",
+ "page-menu.title": "Pàgines",
+ "people-menu.change-color": "Canviar de color",
+ "people-menu.change-name": "Canviar nom",
+ "people-menu.invite": "Convidar altres persones",
+ "people-menu.title": "Gent",
+ "people-menu.user": "(Tu)",
+ "share-menu.copy-link": "Copiar l'enllaç",
+ "share-menu.copy-link-note": "Qualsevol persona que tingui l'enllaç podrà veure i editar aquest projecte.",
+ "share-menu.copy-readonly-link": "Copia l'enllaç de només lectura",
+ "share-menu.copy-readonly-link-note": "Qualsevol persona amb l'enllaç podrà veure (però no editar) aquest projecte.",
+ "share-menu.offline-note": "Si compartiu aquest projecte, es crearà una còpia allotjada en un URL nou. Podeu compartir l'URL amb un màxim de trenta persones més per veure i editar el projecte junts.",
+ "share-menu.project-too-large": "Ho sentim, aquest projecte no es pot compartir perquè és massa gran.",
+ "share-menu.readonly-link": "Només lectura",
+ "share-menu.share-project": "Compartir aquest projecte",
+ "share-menu.title": "Compartir",
+ "shortcuts-dialog.edit": "Editar",
+ "shortcuts-dialog.file": "Fitxer",
+ "shortcuts-dialog.preferences": "Preferències",
+ "shortcuts-dialog.title": "Dreceres de teclat",
+ "shortcuts-dialog.tools": "Eines",
+ "shortcuts-dialog.transform": "Transformar",
+ "shortcuts-dialog.view": "Vista",
+ "size-style.l": "Gran",
+ "size-style.m": "Mitjana",
+ "size-style.s": "Petit",
+ "size-style.xl": "Extra gran",
+ "spline-style.cubic": "Cúbic",
+ "spline-style.line": "Línia",
+ "style-panel.align": "Alinear",
+ "style-panel.arrowheads": "Puntes de fletxa",
+ "style-panel.color": "Color",
+ "style-panel.dash": "Traça",
+ "style-panel.fill": "Pintura",
+ "style-panel.font": "Font",
+ "style-panel.geo": "Forma",
+ "style-panel.mixed": "Mixt",
+ "style-panel.opacity": "Opacitat",
+ "style-panel.size": "Mida",
+ "style-panel.spline": "Spline",
+ "style-panel.title": "Estils",
+ "toast.close": "Tancar",
+ "toast.error.copy-fail.desc": "No s'ha pogut copiar la imatge",
+ "toast.error.copy-fail.title": "No s'ha pogut copiar",
+ "toast.error.export-fail.desc": "No s'ha pogut exportar la imatge",
+ "toast.error.export-fail.title": "No s'ha pogut exportar",
+ "tool-panel.drawing": "Dibuix",
+ "tool-panel.shapes": "Formes",
+ "tool.arrow": "Fletxa",
+ "tool.arrow-down": "Fletxa cap avall",
+ "tool.arrow-left": "Fletxa cap a l'esquerra",
+ "tool.arrow-right": "Fletxa cap a la dreta",
+ "tool.arrow-up": "Fletxa cap amunt",
+ "tool.asset": "Objecte",
+ "tool.diamond": "Diamant",
+ "tool.draw": "Dibuixar",
+ "tool.ellipse": "El·lipse",
+ "tool.embed": "Inserir",
+ "tool.eraser": "Goma d'esborrar",
+ "tool.frame": "Marc",
+ "tool.hand": "Mà",
+ "tool.hexagon": "Hexàgon",
+ "tool.line": "Línia",
+ "tool.note": "Nota",
+ "tool.octagon": "Octàgon",
+ "tool.oval": "Oval",
+ "tool.pentagon": "Pentàgon",
+ "tool.rectangle": "Rectangle",
+ "tool.rhombus": "Rombe",
+ "tool.select": "Seleccionar",
+ "tool.star": "Estrella",
+ "tool.text": "Text",
+ "tool.trapezoid": "Trapezoide",
+ "tool.triangle": "Triangle",
+ "tool.x-box": "Caixa X",
+ "vscode.file-open.desc": "Aquest fitxer es va crear amb una versió anterior de tldraw. Vols actualitzar-lo perquè funcioni amb la nova versió?",
+ "vscode.file-open.dont-show-again": "No preguntar-ho més"
+}
diff --git a/apps/web/public/translations/cs.json b/apps/web/public/translations/cs.json
new file mode 100644
index 00000000..6ef0ce14
--- /dev/null
+++ b/apps/web/public/translations/cs.json
@@ -0,0 +1,352 @@
+{
+ "action.align-bottom": "Zarovnat dolů",
+ "action.align-center-horizontal": "Zarovnat horizontálně",
+ "action.align-center-horizontal.short": "Zarovnat H",
+ "action.align-center-vertical": "Zarovnat vertikálně",
+ "action.align-center-vertical.short": "Zarovnat V",
+ "action.align-left": "Zarovnat doleva",
+ "action.align-right": "Zarovnat doprava",
+ "action.align-top": "Zarovnat nahoru",
+ "action.back-to-content": "Zpět na obsah",
+ "action.bring-forward": "Předložit",
+ "action.bring-to-front": "Přenést do popředí",
+ "action.convert-to-bookmark": "Převést na záložku",
+ "action.convert-to-embed": "Převést na vložení",
+ "action.copy": "Kopírovat",
+ "action.copy-as-json": "Kopírovat jako JSON",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "Kopírovat jako PNG",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "Kopírovat jako SVG",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "Vyjmout",
+ "action.delete": "Odstranit",
+ "action.distribute-horizontal": "Rozdělit horizontálně",
+ "action.distribute-horizontal.short": "Rozdělit H",
+ "action.distribute-vertical": "Rozdělit vertikálně",
+ "action.distribute-vertical.short": "Rozdělit V",
+ "action.duplicate": "Duplikovat",
+ "action.edit-link": "Upravit odkaz",
+ "action.exit-pen-mode": "Ukončete režim pera",
+ "action.export-as-json": "Exportovat jako JSON",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "Exportovat jako PNG",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "Exportovat jako SVG",
+ "action.export-as-svg.short": "SVG",
+ "action.flip-horizontal": "Překlopit vodorovně",
+ "action.flip-horizontal.short": "Otočit H",
+ "action.flip-vertical": "Překlopit svisle",
+ "action.flip-vertical.short": "Otočit V",
+ "action.fork-project": "Rozvětvit projekt",
+ "action.group": "Skupina",
+ "action.insert-embed": "Vložit",
+ "action.insert-media": "Nahrát média",
+ "action.leave-shared-project": "Opustit sdílený projekt",
+ "action.new-project": "Nový projekt",
+ "action.new-shared-project": "Nový sdílený projekt",
+ "action.open-cursor-chat": "Chat s kurzorem",
+ "action.open-embed-link": "Otevřít odkaz",
+ "action.open-file": "Otevřít soubor",
+ "action.pack": "Zabalit",
+ "action.paste": "Vložit",
+ "action.print": "Tisk",
+ "action.redo": "Zpět",
+ "action.remove-frame": "Odstranit rámeček",
+ "action.rotate-ccw": "Otočit proti směru hodinových ručiček",
+ "action.rotate-cw": "Otočit ve směru hodinových ručiček",
+ "action.save-copy": "Uložit kopii",
+ "action.select-all": "Vybrat vše",
+ "action.select-none": "Nevybrat žádné",
+ "action.send-backward": "Poslat zpět",
+ "action.send-to-back": "Odeslat nazpátek",
+ "action.share-project": "Sdílet tento projekt",
+ "action.stack-horizontal": "Zabalit horizontálně",
+ "action.stack-horizontal.short": "Zabalit H",
+ "action.stack-vertical": "Zabalit svisle",
+ "action.stack-vertical.short": "Zabalit V",
+ "action.stop-following": "Přestat sledovat",
+ "action.stretch-horizontal": "Roztáhnout vodorovně",
+ "action.stretch-horizontal.short": "Roztáhnout H",
+ "action.stretch-vertical": "Roztáhnout vertikálně",
+ "action.stretch-vertical.short": "Roztáhnout V",
+ "action.toggle-auto-size": "Přepínání automatické velikosti",
+ "action.toggle-dark-mode": "Zapnout tmavý režim",
+ "action.toggle-dark-mode.menu": "Tmavý režim",
+ "action.toggle-debug-mode": "Zapnout režim ladění",
+ "action.toggle-debug-mode.menu": "Režim ladění",
+ "action.toggle-focus-mode": "Zapnout režim ostření",
+ "action.toggle-focus-mode.menu": "Režim ostření",
+ "action.toggle-grid": "Zapnout mřížku",
+ "action.toggle-grid.menu": "Zobrazit mřížku",
+ "action.toggle-lock": "Zamknout / Odemknout",
+ "action.toggle-reduce-motion": "Přepnout snížení pohybu",
+ "action.toggle-reduce-motion.menu": "Snížení pohybu",
+ "action.toggle-snap-mode": "Vždy zapnout přichycení",
+ "action.toggle-snap-mode.menu": "Vždy přichytit",
+ "action.toggle-tool-lock": "Přepnout zámek nástroje",
+ "action.toggle-tool-lock.menu": "Zámek nástroje",
+ "action.toggle-transparent": "Přepnout průhledné pozadí",
+ "action.toggle-transparent.context-menu": "Transparentní",
+ "action.toggle-transparent.menu": "Transparentní",
+ "action.undo": "Vrátit",
+ "action.ungroup": "Zrušit seskupení",
+ "action.unlock-all": "Odemknout vše",
+ "action.zoom-in": "Přiblížit",
+ "action.zoom-out": "Oddálit",
+ "action.zoom-to-100": "Přiblížit na 100 %",
+ "action.zoom-to-fit": "Přiblížit",
+ "action.zoom-to-selection": "Přiblížit k výběru",
+ "actions-menu.title": "Akce",
+ "align-style.end": "Konec",
+ "align-style.justify": "Zdůvodněte",
+ "align-style.middle": "Střed",
+ "align-style.start": "Začátek",
+ "arrowheadEnd-style.arrow": "Šipka",
+ "arrowheadEnd-style.bar": "Bar",
+ "arrowheadEnd-style.diamond": "Diamant",
+ "arrowheadEnd-style.dot": "Tečka",
+ "arrowheadEnd-style.inverted": "Převrácený",
+ "arrowheadEnd-style.none": "Žádné",
+ "arrowheadEnd-style.pipe": "Potrubí",
+ "arrowheadEnd-style.square": "Čtverec",
+ "arrowheadEnd-style.triangle": "Trojúhelník",
+ "arrowheadStart-style.arrow": "Šipka",
+ "arrowheadStart-style.bar": "Bar",
+ "arrowheadStart-style.diamond": "Diamant",
+ "arrowheadStart-style.dot": "Tečka",
+ "arrowheadStart-style.inverted": "Převrácený",
+ "arrowheadStart-style.none": "Žádné",
+ "arrowheadStart-style.pipe": "Potrubí",
+ "arrowheadStart-style.square": "Čtverec",
+ "arrowheadStart-style.triangle": "Trojúhelník",
+ "color-style.black": "Černá",
+ "color-style.blue": "Modrá",
+ "color-style.green": "Zelená",
+ "color-style.grey": "Šedá",
+ "color-style.light-blue": "Světle modrá",
+ "color-style.light-green": "Světle zelená",
+ "color-style.light-red": "Světle červená",
+ "color-style.light-violet": "Světle fialová",
+ "color-style.orange": "Oranžová",
+ "color-style.red": "Červená",
+ "color-style.violet": "Fialová",
+ "color-style.yellow": "Žlutá",
+ "context-menu.arrange": "Uspořádat",
+ "context-menu.copy-as": "Kopírovat jako",
+ "context-menu.export-as": "Exportovat jako",
+ "context-menu.move-to-page": "Přejít na stránku",
+ "context-menu.reorder": "Přeobjednat",
+ "context.pages.new-page": "Nová stránka",
+ "cursor-chat.type-to-chat": "Napište do chatu...",
+ "dash-style.dashed": "Čárkovaně",
+ "dash-style.dotted": "Tečkovaný",
+ "dash-style.draw": "Kreslit",
+ "dash-style.solid": "Pevný",
+ "debug-panel.more": "Více",
+ "edit-link-dialog.cancel": "Zrušit",
+ "edit-link-dialog.clear": "Smazat",
+ "edit-link-dialog.detail": "Odkazy se otevřou na nové kartě.",
+ "edit-link-dialog.invalid-url": "Odkaz musí být platná adresa URL.",
+ "edit-link-dialog.save": "Pokračovat",
+ "edit-link-dialog.title": "Upravit odkaz",
+ "edit-link-dialog.url": "URL",
+ "edit-pages-dialog.move-down": "Posunout dolů",
+ "edit-pages-dialog.move-up": "Posunout nahoru",
+ "embed-dialog.back": "Zpět",
+ "embed-dialog.cancel": "Zrušit",
+ "embed-dialog.create": "Vytvořit",
+ "embed-dialog.instruction": "Vložte adresu URL webu a vytvořte vložený soubor.",
+ "embed-dialog.invalid-url": "Z této adresy URL se nám nepodařilo vytvořit vložený soubor.",
+ "embed-dialog.title": "Vytvořit vložení",
+ "embed-dialog.url": "URL",
+ "file-system.confirm-clear.cancel": "Zrušit",
+ "file-system.confirm-clear.continue": "Pokračovat",
+ "file-system.confirm-clear.description": "Vytvořením nového projektu vymažete aktuální projekt a všechny neuložené změny budou ztraceny. Opravdu chcete pokračovat?",
+ "file-system.confirm-clear.dont-show-again": "Neptat se znovu",
+ "file-system.confirm-clear.title": "Vymazat aktuální projekt?",
+ "file-system.confirm-open.cancel": "Zrušit",
+ "file-system.confirm-open.description": "Otevřením souboru nahradíte aktuální projekt a všechny neuložené změny budou ztraceny. Opravdu chcete pokračovat?",
+ "file-system.confirm-open.dont-show-again": "Neptat se znovu",
+ "file-system.confirm-open.open": "Otevřít soubor",
+ "file-system.confirm-open.title": "Přepsat aktuální projekt?",
+ "file-system.file-open-error.file-format-version-too-new": "Soubor, který jste se pokusili otevřít, pochází z novější verze tldraw. Znovu načtěte stránku a zkuste to znovu.",
+ "file-system.file-open-error.generic-corrupted-file": "Soubor, který jste se pokusili otevřít, je poškozený.",
+ "file-system.file-open-error.not-a-tldraw-file": "Soubor, který jste se pokusili otevřít, nevypadá jako soubor tldraw.",
+ "file-system.file-open-error.title": "Soubor nelze otevřít",
+ "file-system.shared-document-file-open-error.description": "Otevírání souborů ze sdílených projektů není podporováno.",
+ "file-system.shared-document-file-open-error.title": "Soubor nelze otevřít",
+ "fill-style.none": "Žádné",
+ "fill-style.pattern": "Vzor",
+ "fill-style.semi": "Semi",
+ "fill-style.solid": "Pevný",
+ "focus-mode.toggle-focus-mode": "Přepnout režim ostření",
+ "font-style.draw": "Kreslit",
+ "font-style.mono": "Mono",
+ "font-style.sans": "Sans",
+ "font-style.serif": "Serif",
+ "geo-style.arrow-down": "Šipka dolů",
+ "geo-style.arrow-left": "Šipka doleva",
+ "geo-style.arrow-right": "Šipka doprava",
+ "geo-style.arrow-up": "Šipka nahoru",
+ "geo-style.check-box": "Zaškrtávací políčko",
+ "geo-style.cloud": "Cloud",
+ "geo-style.diamond": "Diamant",
+ "geo-style.ellipse": "Elipsa",
+ "geo-style.hexagon": "Šestiúhelník",
+ "geo-style.octagon": "Osmiúhelník",
+ "geo-style.oval": "Ovál",
+ "geo-style.pentagon": "Pentagon",
+ "geo-style.rectangle": "Obdélník",
+ "geo-style.rhombus": "Kosočtverec",
+ "geo-style.rhombus-2": "Kosočtverec 2",
+ "geo-style.star": "Hvězda",
+ "geo-style.trapezoid": "Lichoběžník",
+ "geo-style.triangle": "Trojúhelník",
+ "geo-style.x-box": "X Box",
+ "help-menu.about": "O",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "Github",
+ "help-menu.keyboard-shortcuts": "Klávesové zkratky",
+ "help-menu.title": "Nápověda a zdroje",
+ "help-menu.twitter": "Twitter",
+ "home-project-dialog.description": "Tohle je Váš domácí projekt. Je pouze pro Vás!",
+ "home-project-dialog.ok": "Ok",
+ "home-project-dialog.title": "Domácí projekt",
+ "menu.copy-as": "Kopírovat jako",
+ "menu.edit": "Upravit",
+ "menu.export-as": "Exportovat jako",
+ "menu.file": "Soubor",
+ "menu.language": "Jazyk",
+ "menu.preferences": "Předvolby",
+ "menu.title": "Menu",
+ "menu.view": "Zobrazit",
+ "navigation-zone.toggle-minimap": "Zapnout minimapu",
+ "navigation-zone.zoom": "Zvětšit",
+ "opacity-style.0.1": "10%",
+ "opacity-style.0.25": "25%",
+ "opacity-style.0.5": "50%",
+ "opacity-style.0.75": "75%",
+ "opacity-style.1": "100%",
+ "page-menu.create-new-page": "Vytvořit novou stránku",
+ "page-menu.edit-done": "Hotovo",
+ "page-menu.edit-start": "Upravit",
+ "page-menu.go-to-page": "Přejít na stránku",
+ "page-menu.max-page-count-reached": "Maximální počet dosažených stránek",
+ "page-menu.new-page-initial-name": "Strana 1",
+ "page-menu.submenu.delete": "Odstranit",
+ "page-menu.submenu.duplicate-page": "Duplikovat",
+ "page-menu.submenu.move-down": "Posunout dolů",
+ "page-menu.submenu.move-up": "Posunout nahoru",
+ "page-menu.submenu.rename": "Přejmenovat",
+ "page-menu.submenu.title": "Menu",
+ "page-menu.title": "Stránky",
+ "people-menu.change-color": "Změnit barvu",
+ "people-menu.change-name": "Změnit jméno",
+ "people-menu.follow": "Sledovat",
+ "people-menu.following": "Sleduji",
+ "people-menu.invite": "Pozvat ostatní",
+ "people-menu.leading": "Sledující",
+ "people-menu.title": "Lidé",
+ "people-menu.user": "(Vy)",
+ "rename-project-dialog.cancel": "Zrušit",
+ "rename-project-dialog.rename": "Přejmenovat",
+ "rename-project-dialog.title": "Přejmenovat projekt",
+ "share-menu.copy-link": "Kopírovat odkaz",
+ "share-menu.copy-link-note": "Kdokoli s odkazem bude moci tento projekt zobrazit a upravit.",
+ "share-menu.copy-readonly-link": "Kopírovat odkaz pouze pro čtení",
+ "share-menu.copy-readonly-link-note": "Kdokoli s odkazem bude moci tento projekt zobrazit (ale nikoli upravit).",
+ "share-menu.create-snapshot-link": "Vytvořit odkaz na snímek",
+ "share-menu.default-project-name": "Sdílený projekt",
+ "share-menu.fork-note": "Vytvořit nový sdílený projekt na základě tohoto snímku.",
+ "share-menu.offline-note": "Sdílením tohoto projektu se vytvoří hostovaná živá kopie na nové adrese URL. Adresu URL můžete sdílet až s třiceti dalšími lidmi, abyste mohli projekt prohlížet a upravovat společně.",
+ "share-menu.project-too-large": "Je nám líto, ale tento projekt nelze sdílet, protože je příliš velký. Pracujeme na tom!",
+ "share-menu.readonly-link": "Pouze pro čtení",
+ "share-menu.save-note": "Stáhněte si tento projekt do počítače jako soubor .tldr",
+ "share-menu.share-project": "Sdílet tento projekt",
+ "share-menu.snapshot-link-note": "Zachyťte a sdílejte tento projekt jako odkaz na snímek pouze pro čtení.",
+ "share-menu.title": "Sdílet",
+ "share-menu.upload-failed": "Omlouváme se, ale v tuto chvíli se nám nepodařilo nahrát váš projekt. Zkuste to prosím znovu nebo nám dejte vědět, pokud problém přetrvává.",
+ "sharing.confirm-leave.cancel": "Zrušit",
+ "sharing.confirm-leave.description": "Jste si jisti, že chcete opustit tento sdílený projekt? Můžete se k němu vrátit přechodem na jeho adresu URL.",
+ "sharing.confirm-leave.dont-show-again": "Znovu se neptat",
+ "sharing.confirm-leave.leave": "Odejít",
+ "sharing.confirm-leave.title": "Opustit současný projekt?",
+ "shortcuts-dialog.collaboration": "Spolupráce",
+ "shortcuts-dialog.edit": "Upravit",
+ "shortcuts-dialog.file": "Soubor",
+ "shortcuts-dialog.preferences": "Předvolby",
+ "shortcuts-dialog.title": "Klávesové zkratky",
+ "shortcuts-dialog.tools": "Nástroje",
+ "shortcuts-dialog.transform": "Transformovat",
+ "shortcuts-dialog.view": "Zobrazit",
+ "size-style.l": "Velký",
+ "size-style.m": "Střední",
+ "size-style.s": "Malý",
+ "size-style.xl": "Extra velké",
+ "spline-style.cubic": "Krychlový",
+ "spline-style.line": "Čára",
+ "status.offline": "Offline",
+ "status.online": "Online",
+ "style-panel.align": "Zarovnat",
+ "style-panel.arrowhead-end": "Konec",
+ "style-panel.arrowhead-start": "Začátek",
+ "style-panel.arrowheads": "Šipky",
+ "style-panel.color": "Barva",
+ "style-panel.dash": "Pomlčka",
+ "style-panel.fill": "Vyplnit",
+ "style-panel.font": "Písmo",
+ "style-panel.geo": "Tvar",
+ "style-panel.mixed": "Smíšené",
+ "style-panel.opacity": "Neprůhlednost",
+ "style-panel.position": "Pozice",
+ "style-panel.size": "Velikost",
+ "style-panel.spline": "Drážkování",
+ "style-panel.title": "Styly",
+ "style-panel.vertical-align": "Svislé zarovnání",
+ "toast.close": "Zavřít",
+ "toast.error.copy-fail.desc": "Kopírování obrázku se nezdařilo",
+ "toast.error.copy-fail.title": "Kopírování se nezdařilo",
+ "toast.error.export-fail.desc": "Export obrázku se nezdařil",
+ "toast.error.export-fail.title": "Export se nezdařil",
+ "tool-panel.drawing": "Výkres",
+ "tool-panel.more": "Více",
+ "tool-panel.shapes": "Tvary",
+ "tool.arrow": "Šipka",
+ "tool.arrow-down": "Šipka dolů",
+ "tool.arrow-left": "Šipka doleva",
+ "tool.arrow-right": "Šipka doprava",
+ "tool.arrow-up": "Šipka nahoru",
+ "tool.asset": "Aktivum",
+ "tool.check-box": "Zaškrtávací políčko",
+ "tool.cloud": "Cloud",
+ "tool.diamond": "Diamant",
+ "tool.draw": "Kreslit",
+ "tool.ellipse": "Elipsa",
+ "tool.embed": "Vložit",
+ "tool.eraser": "Guma",
+ "tool.frame": "Rám",
+ "tool.hand": "Ruka",
+ "tool.hexagon": "Šestiúhelník",
+ "tool.highlight": "Zvýraznit",
+ "tool.laser": "Laser",
+ "tool.line": "Čára",
+ "tool.note": "Poznámka",
+ "tool.octagon": "Osmiúhelník",
+ "tool.oval": "Ovál",
+ "tool.pentagon": "Pentagon",
+ "tool.rectangle": "Obdélník",
+ "tool.rhombus": "Kosočtverec",
+ "tool.select": "Vybrat",
+ "tool.star": "Hvězda",
+ "tool.text": "Text",
+ "tool.trapezoid": "Lichoběžník",
+ "tool.triangle": "Trojúhelník",
+ "tool.x-box": "X box",
+ "vscode.file-open.backup": "Záloha",
+ "vscode.file-open.backup-failed": "Zálohování se nezdařilo: nejedná se o soubor .tldr",
+ "vscode.file-open.backup-saved": "Záloha uložena",
+ "vscode.file-open.desc": "Tento soubor byl vytvořen pomocí starší verze tldraw. Chcete jej aktualizovat, aby fungoval s novou verzí?",
+ "vscode.file-open.dont-show-again": "Neptat se znovu",
+ "vscode.file-open.open": "Pokračovat"
+}
diff --git a/apps/web/public/translations/da.json b/apps/web/public/translations/da.json
new file mode 100644
index 00000000..27d2b03c
--- /dev/null
+++ b/apps/web/public/translations/da.json
@@ -0,0 +1,160 @@
+{
+ "action.convert-to-bookmark": "Konverter til bogmærke",
+ "action.copy": "Kopier",
+ "action.cut": "Klip",
+ "action.delete": "Slet",
+ "action.duplicate": "Dupliker",
+ "action.flip-horizontal": "Vend vandret",
+ "action.flip-vertical": "Vend lodret",
+ "action.group": "Grupper",
+ "action.insert-media": "Upload medie",
+ "action.paste": "Indsæt",
+ "action.redo": "Gentag",
+ "action.select-all": "Vælg alt",
+ "action.select-none": "Fravælg alt",
+ "action.undo": "Fortryd",
+ "action.ungroup": "Opdel gruppe",
+ "action.zoom-in": "Zoom ind",
+ "action.zoom-out": "Zoom ud",
+ "action.zoom-to-fit": "Zoom til lærred",
+ "action.zoom-to-selection": "Zoom til valgte",
+ "actions-menu.title": "Handlinger",
+ "arrowheadEnd-style.arrow": "Pil",
+ "arrowheadEnd-style.none": "Ingen",
+ "arrowheadEnd-style.triangle": "Trekant",
+ "arrowheadStart-style.arrow": "Pil",
+ "arrowheadStart-style.bar": "Bar",
+ "arrowheadStart-style.diamond": "Diamant",
+ "arrowheadStart-style.dot": "Prik",
+ "arrowheadStart-style.inverted": "Inverteret",
+ "arrowheadStart-style.none": "Ingen",
+ "arrowheadStart-style.pipe": "Rør",
+ "arrowheadStart-style.square": "Firkant",
+ "arrowheadStart-style.triangle": "Trekant",
+ "context-menu.arrange": "Arranger",
+ "context-menu.copy-as": "Kopier som",
+ "context-menu.export-as": "Eksporter som",
+ "context-menu.move-to-page": "Flyt til side",
+ "context-menu.reorder": "Omarrangere",
+ "context.pages.new-page": "Ny side",
+ "dash-style.draw": "Tegn",
+ "edit-link-dialog.cancel": "Annuler",
+ "edit-link-dialog.clear": "Fjern",
+ "edit-link-dialog.detail": "Links vil åbne i nyefaner.",
+ "edit-link-dialog.invalid-url": "Et link skal være et gyldigt URL",
+ "edit-link-dialog.save": "Fortsæt",
+ "edit-link-dialog.title": "Rediger link",
+ "edit-link-dialog.url": "URL",
+ "edit-pages-dialog.move-down": "Flyt ned",
+ "edit-pages-dialog.move-up": "Ryg op",
+ "embed-dialog.back": "Tilbage",
+ "embed-dialog.cancel": "Annuller",
+ "embed-dialog.create": "Opret",
+ "embed-dialog.instruction": "Indsæt webstedets URL for at oprette indlejringen.",
+ "embed-dialog.invalid-url": "Vi kunne ikke oprette en indlejring fra denne URL.",
+ "embed-dialog.title": "Opret indlejring",
+ "embed-dialog.url": "URL",
+ "file-system.confirm-open.cancel": "Annuller",
+ "file-system.confirm-open.description": "Åbning af fil vil erstatte dit nuværende projekt, og alt ugemte ændringer vil gå tabt. Er du sikker på, at du vil fortsætte?",
+ "file-system.confirm-open.dont-show-again": "Spørg ikke igen",
+ "file-system.confirm-open.open": "Åben fil",
+ "file-system.confirm-open.title": "Overskriv nuværende projekt",
+ "file-system.file-open-error.file-format-version-too-new": "Filen du prøver at åbne er fra en nyere version af tldraw. Vær venlig at genindlæs siden og prøv igen.",
+ "file-system.file-open-error.generic-corrupted-file": "Filen du forsøger at åbne er beskadiget.",
+ "file-system.file-open-error.not-a-tldraw-file": "Filen, du forsøgte at åbne, ligner ikke en tldraw-fil.",
+ "file-system.file-open-error.title": "Kunne ikke åbne filen",
+ "file-system.shared-document-file-open-error.description": "Åbning af filer fra delte projekter er ikke understøttet.",
+ "file-system.shared-document-file-open-error.title": "Kunne ikke åbne filen",
+ "focus-mode.toggle-focus-mode": "Skift fokustilstand",
+ "font-style.draw": "Tegn",
+ "geo-style.ellipse": "Ellipse",
+ "geo-style.rectangle": "Rektangel",
+ "geo-style.triangle": "Trekant",
+ "geo-style.x-box": "X Boks",
+ "help-menu.about": "Om",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "Tastaturgenveje",
+ "help-menu.title": "Hjælp og ressourcer",
+ "help-menu.twitter": "Twitter",
+ "menu.copy-as": "Kopier som",
+ "menu.edit": "Rediger",
+ "menu.export-as": "Eksporter som",
+ "menu.file": "Fil",
+ "menu.language": "Sprog",
+ "menu.preferences": "Indstillinger",
+ "menu.title": "Menu",
+ "menu.view": "Vis",
+ "navigation-zone.toggle-minimap": "Skift minikort til",
+ "navigation-zone.zoom": "Zoom ind",
+ "page-menu.create-new-page": "Opret ny side",
+ "page-menu.edit-done": "Færdig",
+ "page-menu.edit-start": "Rediger",
+ "page-menu.max-page-count-reached": "Højst antal sider nået",
+ "page-menu.new-page-initial-name": "Side 1",
+ "page-menu.submenu.delete": "Slet",
+ "page-menu.submenu.duplicate-page": "Dupliker",
+ "page-menu.submenu.move-down": "Flyt ned",
+ "page-menu.submenu.move-up": "Flyt op",
+ "page-menu.submenu.rename": "Omdøb",
+ "page-menu.submenu.title": "Menu",
+ "page-menu.title": "Sider",
+ "people-menu.change-color": "Skift farve",
+ "people-menu.change-name": "Ændre navn",
+ "people-menu.invite": "Inviter andre",
+ "people-menu.title": "Folk",
+ "people-menu.user": "(Dig)",
+ "share-menu.copy-link": "Kopier linket",
+ "share-menu.copy-link-note": "Alle med linket vil være i stand til at se og rediger projektet.",
+ "share-menu.copy-readonly-link": "Kopier skrivebeskyttet link",
+ "share-menu.copy-readonly-link-note": "Alle med linket vil kunne se (men ikke ændre) dette projekt.",
+ "share-menu.offline-note": "Deling af dette projekt vil oprette en hosted live-kopi på opgivet URL. Du can dele URL'en med op til 30 andre mennesker som kan se og ændre projektet sammen.",
+ "share-menu.project-too-large": "Beklager, dette projekt kan ikke deles, fordi det er for stort. Vi arbejder på det.",
+ "share-menu.readonly-link": "Skrivebeskyttet",
+ "share-menu.share-project": "Del dette projekt",
+ "share-menu.title": "Del",
+ "shortcuts-dialog.edit": "Rediger",
+ "shortcuts-dialog.file": "Fil",
+ "shortcuts-dialog.preferences": "Indstillinger",
+ "shortcuts-dialog.title": "Tastaturgenveje",
+ "shortcuts-dialog.tools": "Værktøjer",
+ "shortcuts-dialog.transform": "Flyt & Skalér",
+ "shortcuts-dialog.view": "Vis",
+ "spline-style.line": "Linje",
+ "style-panel.align": "Juster",
+ "style-panel.arrowheads": "Pilehoveder",
+ "style-panel.color": "Farve",
+ "style-panel.dash": "Streg",
+ "style-panel.fill": "Fyld",
+ "style-panel.font": "Skrifttype",
+ "style-panel.geo": "Figur",
+ "style-panel.mixed": "Blandet",
+ "style-panel.opacity": "Gennemsigtighed",
+ "style-panel.size": "Størrelse",
+ "style-panel.spline": "Spline",
+ "style-panel.title": "Format",
+ "toast.close": "Luk",
+ "toast.error.copy-fail.desc": "Kunne ikke kopiere billedet",
+ "toast.error.copy-fail.title": "Mislykket kopiere",
+ "toast.error.export-fail.desc": "Billedet kunne ikke eksporteres",
+ "toast.error.export-fail.title": "Mislykket eksport",
+ "tool-panel.drawing": "Tegning",
+ "tool-panel.shapes": "Figurer",
+ "tool.arrow": "Pil",
+ "tool.asset": "Aktiv",
+ "tool.draw": "Tegn",
+ "tool.ellipse": "Ellipse",
+ "tool.embed": "Indlejre",
+ "tool.eraser": "Viskelæder",
+ "tool.frame": "Ramme",
+ "tool.line": "Linje",
+ "tool.note": "Note",
+ "tool.rectangle": "Rektangel",
+ "tool.select": "Vælg",
+ "tool.text": "Tekst",
+ "tool.trapezoid": "Trapezoid",
+ "tool.triangle": "Trekant",
+ "tool.x-box": "X boks",
+ "vscode.file-open.desc": "Denne fil blev oprettet med en tidligere version af tldraw. Vil du opdatere filen så den virker med den nye version?",
+ "vscode.file-open.dont-show-again": "Spørg ikke igen"
+}
diff --git a/apps/web/public/translations/de.json b/apps/web/public/translations/de.json
new file mode 100644
index 00000000..92a26fa3
--- /dev/null
+++ b/apps/web/public/translations/de.json
@@ -0,0 +1,310 @@
+{
+ "action.align-bottom": "Unten ausrichten",
+ "action.align-center-horizontal": "Horizontal ausrichten",
+ "action.align-center-horizontal.short": "Horizontal ausrichten",
+ "action.align-center-vertical": "Vertikal ausrichten",
+ "action.align-center-vertical.short": "Vertikal ausrichten",
+ "action.align-left": "Links ausrichten",
+ "action.align-right": "Rechts ausrichten",
+ "action.align-top": "Oben ausrichten",
+ "action.back-to-content": "Zurück zum Inhalt",
+ "action.bring-forward": "Vorziehen",
+ "action.bring-to-front": "Nach vorne bringen",
+ "action.convert-to-bookmark": "In Lesezeichen umwandeln",
+ "action.convert-to-embed": "In Einbettung umwandeln",
+ "action.copy": "Kopieren",
+ "action.copy-as-json": "Kopieren als JSON",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "Als PNG kopieren",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "Als SVG kopieren",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "Ausschneiden",
+ "action.delete": "Löschen",
+ "action.distribute-horizontal": "Horizontal verteilen",
+ "action.distribute-horizontal.short": "Horizontal verteilen",
+ "action.distribute-vertical": "Vertikal verteilen",
+ "action.distribute-vertical.short": "Vertikal verteilen",
+ "action.duplicate": "Duplizieren",
+ "action.edit-link": "Link bearbeiten",
+ "action.exit-pen-mode": "Stiftmodus beenden",
+ "action.export-as-json": "Exportieren als JSON",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "Als PNG exportieren",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "Exportieren als SVG",
+ "action.export-as-svg.short": "SVG",
+ "action.flip-horizontal": "Horizontal flippen",
+ "action.flip-horizontal.short": "Horizontal flippen",
+ "action.flip-vertical": "Vertikal flippen",
+ "action.flip-vertical.short": "Vertikal flippen",
+ "action.group": "Gruppe",
+ "action.insert-embed": "Einbettung einfügen",
+ "action.insert-media": "Medien hochladen",
+ "action.new-project": "Neues Projekt",
+ "action.new-shared-project": "Neues gemeinsames Projekt",
+ "action.open-cursor-chat": "Cursor-Chat",
+ "action.open-embed-link": "Link öffnen",
+ "action.open-file": "Datei öffnen",
+ "action.pack": "Verpacken",
+ "action.paste": "Einfügen",
+ "action.print": "Drucken",
+ "action.redo": "Wiederholen",
+ "action.rotate-ccw": "Drehen gegen den Uhrzeigersinn",
+ "action.rotate-cw": "Drehen im Uhrzeigersinn",
+ "action.save-copy": "Eine Kopie speichern",
+ "action.select-all": "Alle auswählen",
+ "action.select-none": "Keine auswählen",
+ "action.send-backward": "Rückwärts senden",
+ "action.send-to-back": "Zurücksenden",
+ "action.share-project": "Dieses Projekt teilen",
+ "action.stack-horizontal": "Horizontal stapeln",
+ "action.stack-horizontal.short": "Horizontal stapeln",
+ "action.stack-vertical": "Vertikal stapeln",
+ "action.stack-vertical.short": "Vertikal stapeln",
+ "action.stretch-horizontal": "Horizontal auseinanderziehen",
+ "action.stretch-horizontal.short": "Horizontal auseinanderziehen",
+ "action.stretch-vertical": "Vertikal auseinanderziehen",
+ "action.stretch-vertical.short": "Vertikal auseinanderziehen",
+ "action.toggle-auto-size": "Automatische Größe umschalten",
+ "action.toggle-dark-mode": "Dunkelmodus umschalten",
+ "action.toggle-dark-mode.menu": "Dunkelmodus",
+ "action.toggle-debug-mode": "Debug-Modus umschalten",
+ "action.toggle-debug-mode.menu": "Debug-Modus",
+ "action.toggle-focus-mode": "Fokus-Modus umschalten",
+ "action.toggle-focus-mode.menu": "Fokus-Modus",
+ "action.toggle-grid": "Gitter umschalten",
+ "action.toggle-grid.menu": "Gitter anzeigen",
+ "action.toggle-lock": "Sperren / Entsperren",
+ "action.toggle-reduce-motion": "Bewegung reduzieren ein / aus",
+ "action.toggle-reduce-motion.menu": "Bewegung reduzieren ",
+ "action.toggle-snap-mode": "Immer einrasten umschalten",
+ "action.toggle-snap-mode.menu": "Immer an anderen Elementen ausrichten",
+ "action.toggle-tool-lock": "Tool-Sperre umschalten",
+ "action.toggle-tool-lock.menu": "Tool-Sperre",
+ "action.toggle-transparent": "Transparenten Hintergrund ein- und ausschalten",
+ "action.toggle-transparent.context-menu": "Transparent",
+ "action.toggle-transparent.menu": "Transparent",
+ "action.undo": "Rückgängig",
+ "action.ungroup": "Gruppierung auflösen",
+ "action.unlock-all": "Alle entsperren",
+ "action.zoom-in": "Vergrößern",
+ "action.zoom-out": "Verkleinern",
+ "action.zoom-to-100": "Auf 100 % zoomen",
+ "action.zoom-to-fit": "Zoom auf Inhalt anpassen",
+ "action.zoom-to-selection": "Zoom auf Auswahl",
+ "actions-menu.title": "Aktionen",
+ "align-style.end": "Beenden",
+ "align-style.justify": "Rechtfertigen",
+ "align-style.middle": "Mitte",
+ "align-style.start": "Start",
+ "arrowheadEnd-style.arrow": "Pfeil",
+ "arrowheadEnd-style.bar": "Leiste",
+ "arrowheadEnd-style.diamond": "Diamant",
+ "arrowheadEnd-style.dot": "Punkt",
+ "arrowheadEnd-style.inverted": "Umgekehrt",
+ "arrowheadEnd-style.none": "Keine",
+ "arrowheadEnd-style.pipe": "Rohr",
+ "arrowheadEnd-style.square": "Quadrat",
+ "arrowheadEnd-style.triangle": "Dreieck",
+ "arrowheadStart-style.arrow": "Pfeil",
+ "arrowheadStart-style.bar": "Leiste",
+ "arrowheadStart-style.diamond": "Diamant",
+ "arrowheadStart-style.dot": "Punkt",
+ "arrowheadStart-style.inverted": "Umgekehrt",
+ "arrowheadStart-style.none": "Keine",
+ "arrowheadStart-style.pipe": "Rohr",
+ "arrowheadStart-style.square": "Quadrat",
+ "arrowheadStart-style.triangle": "Dreieck",
+ "color-style.black": "Schwarz",
+ "color-style.blue": "Blau",
+ "color-style.green": "Grün",
+ "color-style.grey": "Grau",
+ "color-style.light-blue": "Hellblau",
+ "color-style.light-green": "Hellgrün",
+ "color-style.light-red": "Hellrot",
+ "color-style.light-violet": "Hellviolett",
+ "color-style.orange": "Orange",
+ "color-style.red": "Rot",
+ "color-style.violet": "Lila",
+ "color-style.yellow": "Gelb",
+ "context-menu.arrange": "Anordnen",
+ "context-menu.copy-as": "Kopieren als",
+ "context-menu.export-as": "Exportieren als",
+ "context-menu.move-to-page": "Weiter zur Seite",
+ "context-menu.reorder": "Neu anordnen",
+ "context.pages.new-page": "Neue Seite",
+ "cursor-chat.type-to-chat": "Tippen, um zu chatten...",
+ "dash-style.dashed": "Gestrichelt",
+ "dash-style.dotted": "Gepunkted",
+ "dash-style.draw": "Zeichnen",
+ "dash-style.solid": "Solide",
+ "debug-panel.more": "Mehr",
+ "edit-link-dialog.cancel": "Abbrechen",
+ "edit-link-dialog.clear": "Löschen",
+ "edit-link-dialog.detail": "Die Links werden in einer neuen Registerkarte geöffnet.",
+ "edit-link-dialog.invalid-url": "Ein Link muss eine gültige URL sein.",
+ "edit-link-dialog.save": "Weiter",
+ "edit-link-dialog.title": "Link bearbeiten",
+ "edit-link-dialog.url": "URL",
+ "edit-pages-dialog.move-down": "Nach unten",
+ "edit-pages-dialog.move-up": "Nach oben",
+ "embed-dialog.back": "Zurück",
+ "embed-dialog.cancel": "Abbrechen",
+ "embed-dialog.create": "Erstellen",
+ "embed-dialog.instruction": "Fügen Sie die URL der Website ein, um die Einbettung einzufügen.",
+ "embed-dialog.invalid-url": "Wir konnten keine Einbettung von dieser URL erstellen.",
+ "embed-dialog.title": "Einbettung erstellen",
+ "embed-dialog.url": "URL",
+ "file-system.confirm-open.cancel": "Abbrechen",
+ "file-system.confirm-open.description": "Wenn Sie eine Datei öffnen, wird Ihr aktuelles Projekt ersetzt und alle nicht gespeicherten Änderungen gehen verloren. Sind Sie sicher, dass Sie fortfahren möchten?",
+ "file-system.confirm-open.dont-show-again": "Nicht nochmal fragen",
+ "file-system.confirm-open.open": "Datei öffnen",
+ "file-system.confirm-open.title": "Aktuelles Projekt überschreiben?",
+ "file-system.file-open-error.file-format-version-too-new": "Die Datei, die Sie zu öffnen versucht haben, ist von einer neueren Version von tldraw. Bitte laden Sie die Seite neu und versuchen Sie es erneut.",
+ "file-system.file-open-error.generic-corrupted-file": "Die Datei, die Sie zu öffnen versucht haben, ist beschädigt.",
+ "file-system.file-open-error.not-a-tldraw-file": "Die Datei, die Sie zu öffnen versucht haben, sieht nicht wie eine tldraw-Datei aus.",
+ "file-system.file-open-error.title": "Datei konnte nicht geöffnet werden",
+ "file-system.shared-document-file-open-error.title": "Konnte die Datei nicht öffnen",
+ "fill-style.none": "Keine",
+ "fill-style.pattern": "Muster",
+ "fill-style.semi": "Semi",
+ "fill-style.solid": "Solide",
+ "focus-mode.toggle-focus-mode": "Fokus-Modus umschalten",
+ "font-style.draw": "Zeichnen",
+ "font-style.mono": "Mono",
+ "font-style.sans": "Sans",
+ "font-style.serif": "Serif",
+ "geo-style.arrow-down": "Pfeil nach unten",
+ "geo-style.arrow-left": "Pfeil nach links",
+ "geo-style.arrow-right": "Pfeil nach rechts",
+ "geo-style.arrow-up": "Pfeil nach oben",
+ "geo-style.check-box": "Haken Box",
+ "geo-style.cloud": "Wolke",
+ "geo-style.diamond": "Diamant",
+ "geo-style.ellipse": "Ellipse",
+ "geo-style.hexagon": "Sechseck",
+ "geo-style.octagon": "Achteck",
+ "geo-style.oval": "Oval",
+ "geo-style.pentagon": "Fünfeck",
+ "geo-style.rectangle": "Rechteck",
+ "geo-style.rhombus": "Rhombus",
+ "geo-style.rhombus-2": "Rhombus 2",
+ "geo-style.star": "Stern",
+ "geo-style.trapezoid": "Trapez",
+ "geo-style.triangle": "Dreieck",
+ "geo-style.x-box": "X Box",
+ "help-menu.about": "Über",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "Tastaturkürzel",
+ "help-menu.title": "Hilfe und Ressourcen",
+ "help-menu.twitter": "Twitter",
+ "home-project-dialog.ok": "Okay",
+ "menu.copy-as": "Kopieren als",
+ "menu.edit": "Bearbeiten",
+ "menu.export-as": "Exportieren als",
+ "menu.file": "Datei",
+ "menu.language": "Sprache",
+ "menu.preferences": "Einstellungen",
+ "menu.title": "Menü",
+ "menu.view": "Anzeigen",
+ "navigation-zone.toggle-minimap": "Minimap ein- und ausblenden",
+ "navigation-zone.zoom": "Zoomen",
+ "opacity-style.0.1": "10 %",
+ "opacity-style.0.25": "25 %",
+ "opacity-style.0.5": "50 %",
+ "opacity-style.0.75": "75 %",
+ "opacity-style.1": "100 %",
+ "page-menu.create-new-page": "Neue Seite erstellen",
+ "page-menu.edit-done": "Fertig",
+ "page-menu.edit-start": "Bearbeiten",
+ "page-menu.go-to-page": "Gehe zu Seite",
+ "page-menu.max-page-count-reached": "Maximale Seitenzahl erreicht",
+ "page-menu.new-page-initial-name": "Seite 1",
+ "page-menu.submenu.delete": "Löschen",
+ "page-menu.submenu.duplicate-page": "Duplizieren",
+ "page-menu.submenu.move-down": "Nach unten",
+ "page-menu.submenu.move-up": "Nach oben",
+ "page-menu.submenu.rename": "Umbenennen",
+ "page-menu.submenu.title": "Menü",
+ "page-menu.title": "Seiten",
+ "people-menu.change-color": "Farbe ändern",
+ "people-menu.change-name": "Name ändern",
+ "people-menu.invite": "Andere einladen",
+ "people-menu.title": "Leute",
+ "people-menu.user": "(Sie)",
+ "share-menu.copy-link": "Link kopieren",
+ "share-menu.copy-link-note": "Jeder, der den Link hat, kann dieses Projekt ansehen und bearbeiten.",
+ "share-menu.copy-readonly-link": "Schreibgeschützten Link kopieren",
+ "share-menu.copy-readonly-link-note": "Jeder, der den Link hat, kann dieses Projekt sehen (aber nicht bearbeiten).",
+ "share-menu.offline-note": "Wenn Sie dieses Projekt freigeben, wird eine gehostete Live-Kopie unter einer neuen URL erstellt. Sie können die URL mit bis zu dreißig anderen Personen teilen, um das Projekt gemeinsam anzuzeigen und zu bearbeiten.",
+ "share-menu.project-too-large": "Dieses Projekt kann leider nicht geteilt werden, da es zu groß ist. Wir arbeiten daran!",
+ "share-menu.readonly-link": "Schreibgeschützt",
+ "share-menu.share-project": "Dieses Projekt teilen",
+ "share-menu.title": "Teilen",
+ "shortcuts-dialog.collaboration": "Zusammenarbeit",
+ "shortcuts-dialog.edit": "Bearbeiten",
+ "shortcuts-dialog.file": "Datei",
+ "shortcuts-dialog.preferences": "Einstellungen",
+ "shortcuts-dialog.title": "Tastaturkürzel",
+ "shortcuts-dialog.tools": "Tools",
+ "shortcuts-dialog.transform": "Transformieren",
+ "shortcuts-dialog.view": "Anzeigen",
+ "size-style.l": "Groß",
+ "size-style.m": "Mittel",
+ "size-style.s": "Klein",
+ "size-style.xl": "Extra groß",
+ "spline-style.cubic": "Würfel",
+ "spline-style.line": "Linie",
+ "style-panel.align": "Ausrichten",
+ "style-panel.arrowheads": "Pfeilspitzen",
+ "style-panel.color": "Farbe",
+ "style-panel.dash": "Dash",
+ "style-panel.fill": "Füllen",
+ "style-panel.font": "Schriftart",
+ "style-panel.geo": "Form",
+ "style-panel.mixed": "Gemischt",
+ "style-panel.opacity": "Deckkraft",
+ "style-panel.size": "Größe",
+ "style-panel.spline": "Spline",
+ "style-panel.title": "Styles",
+ "toast.close": "Schließen",
+ "toast.error.copy-fail.desc": "Bild kann nicht kopiert werden",
+ "toast.error.copy-fail.title": "Kopieren fehlgeschlagen",
+ "toast.error.export-fail.desc": "Bild kann nicht exportiert werden",
+ "toast.error.export-fail.title": "Exportieren fehlgeschlagen",
+ "tool-panel.drawing": "Zeichnung",
+ "tool-panel.more": "Mehr",
+ "tool-panel.shapes": "Formen",
+ "tool.arrow": "Pfeil",
+ "tool.arrow-down": "Pfeil nach unten",
+ "tool.arrow-left": "Pfeil nach links",
+ "tool.arrow-right": "Pfeil nach rechts",
+ "tool.arrow-up": "Pfeil nach oben",
+ "tool.asset": "Asset",
+ "tool.check-box": "Haken Box",
+ "tool.cloud": "Wolke",
+ "tool.diamond": "Diamant",
+ "tool.draw": "Zeichnen",
+ "tool.ellipse": "Ellipse",
+ "tool.embed": "Einbetten",
+ "tool.eraser": "Radiergummi",
+ "tool.frame": "Rahmen",
+ "tool.hand": "Hand",
+ "tool.hexagon": "Sechseck",
+ "tool.highlight": "Markieren",
+ "tool.line": "Linie",
+ "tool.note": "Hinweis",
+ "tool.octagon": "Achteck",
+ "tool.oval": "Oval",
+ "tool.pentagon": "Fünfeck",
+ "tool.rectangle": "Rechteck",
+ "tool.rhombus": "Rhombus",
+ "tool.select": "Auswählen",
+ "tool.star": "Stern",
+ "tool.text": "Text",
+ "tool.trapezoid": "Trapez",
+ "tool.triangle": "Dreieck",
+ "tool.x-box": "X Box"
+}
diff --git a/apps/web/public/translations/en.json b/apps/web/public/translations/en.json
new file mode 100644
index 00000000..0967ef42
--- /dev/null
+++ b/apps/web/public/translations/en.json
@@ -0,0 +1 @@
+{}
diff --git a/apps/web/public/translations/es.json b/apps/web/public/translations/es.json
new file mode 100644
index 00000000..d51fe408
--- /dev/null
+++ b/apps/web/public/translations/es.json
@@ -0,0 +1,299 @@
+{
+ "action.align-bottom": "Alinear abajo",
+ "action.align-center-horizontal": "Alinear horizontalmente",
+ "action.align-center-horizontal.short": "Alinear H",
+ "action.align-center-vertical": "Alinear verticalmente",
+ "action.align-center-vertical.short": "Alinear V",
+ "action.align-left": "Alinear a la izquierda",
+ "action.align-right": "Alinear a la derecha",
+ "action.align-top": "Alinear arriba",
+ "action.back-to-content": "Volver al contenido",
+ "action.bring-forward": "Traer hacia adelante",
+ "action.bring-to-front": "Traer al frente",
+ "action.convert-to-bookmark": "Convertir en marcador",
+ "action.convert-to-embed": "Convertir en incrustación",
+ "action.copy": "Copiar",
+ "action.copy-as-json": "Copiar como JSON",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "Copiar como PNG",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "Copiar como SVG",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "Cortar",
+ "action.delete": "Eliminar",
+ "action.distribute-horizontal": "Distribuir horizontalmente",
+ "action.distribute-horizontal.short": "Distribuir H",
+ "action.distribute-vertical": "Distribuir verticalmente",
+ "action.distribute-vertical.short": "Distribuir V",
+ "action.duplicate": "Duplicar",
+ "action.edit-link": "Editar enlace",
+ "action.exit-pen-mode": "Salir de modo lápiz",
+ "action.export-as-json": "Exportar como JSON",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "Exportar como PNG",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "Exportar como SVG",
+ "action.export-as-svg.short": "SVG",
+ "action.flip-horizontal": "Voltear horizontalmente",
+ "action.flip-horizontal.short": "Voltear H",
+ "action.flip-vertical": "Voltear verticalmente",
+ "action.flip-vertical.short": "Voltear V",
+ "action.group": "Agrupar",
+ "action.insert-embed": "Insertar incrustación",
+ "action.insert-media": "Cargar contenido multimedia",
+ "action.new-shared-project": "Nuevo proyecto compartido",
+ "action.open-embed-link": "Abrir enlace",
+ "action.open-file": "Abrir archivo",
+ "action.pack": "Envasar",
+ "action.paste": "Pegar",
+ "action.print": "Imprimir",
+ "action.redo": "Rehacer",
+ "action.rotate-ccw": "Girar en el sentido contrario de las agujas del reloj",
+ "action.rotate-cw": "Girar en el sentido de las agujas del reloj",
+ "action.save-copy": "Guardar una copia",
+ "action.select-all": "Seleccionar todo",
+ "action.select-none": "Deseleccionar todo",
+ "action.send-backward": "Enviar hacia atrás",
+ "action.send-to-back": "Enviar al fondo",
+ "action.share-project": "Compartir este proyecto",
+ "action.stack-horizontal": "Apilar horizontalmente",
+ "action.stack-horizontal.short": "Apilar H",
+ "action.stack-vertical": "Apilar verticalmente",
+ "action.stack-vertical.short": "Apilar V",
+ "action.stretch-horizontal": "Estirar horizontalmente",
+ "action.stretch-horizontal.short": "Estirar H",
+ "action.stretch-vertical": "Estirar verticalmente",
+ "action.stretch-vertical.short": "Estirar V",
+ "action.toggle-auto-size": "Alternar tamaño automático",
+ "action.toggle-dark-mode": "Alternar modo oscuro",
+ "action.toggle-dark-mode.menu": "Modo oscuro",
+ "action.toggle-debug-mode": "Alternar modo de depuración",
+ "action.toggle-debug-mode.menu": "Modo de depuración",
+ "action.toggle-focus-mode": "Alternar modo de enfoque",
+ "action.toggle-focus-mode.menu": "Modo de enfoque",
+ "action.toggle-grid": "Alternar cuadrícula",
+ "action.toggle-grid.menu": "Mostrar cuadrícula",
+ "action.toggle-reduce-motion": "Reducir movimiento",
+ "action.toggle-reduce-motion.menu": "Reducir movimiento",
+ "action.toggle-snap-mode": "Alternar ajustar siempre",
+ "action.toggle-snap-mode.menu": "Ajustar a cuadrícula",
+ "action.toggle-tool-lock": "Alternar bloqueo de herramienta",
+ "action.toggle-tool-lock.menu": "Alternar bloqueo",
+ "action.toggle-transparent": "Alternar fondo transparente",
+ "action.toggle-transparent.context-menu": "Transparente",
+ "action.toggle-transparent.menu": "Transparente",
+ "action.undo": "Deshacer",
+ "action.ungroup": "Desagrupar",
+ "action.unlock-all": "Desbloquear todo",
+ "action.zoom-in": "Acercar",
+ "action.zoom-out": "Alejar",
+ "action.zoom-to-100": "Zoom a 100 %",
+ "action.zoom-to-fit": "Zoom para ajustar",
+ "action.zoom-to-selection": "Zoom a la selección",
+ "actions-menu.title": "Acciones",
+ "align-style.end": "Final",
+ "align-style.justify": "Justificar",
+ "align-style.middle": "Medio",
+ "align-style.start": "Inicio",
+ "arrowheadEnd-style.arrow": "Flecha",
+ "arrowheadEnd-style.bar": "Barra",
+ "arrowheadEnd-style.diamond": "Diamante",
+ "arrowheadEnd-style.dot": "Punto",
+ "arrowheadEnd-style.inverted": "Invertido",
+ "arrowheadEnd-style.none": "Ninguno",
+ "arrowheadEnd-style.pipe": "Tubo",
+ "arrowheadEnd-style.square": "Cuadrado",
+ "arrowheadEnd-style.triangle": "Triángulo",
+ "arrowheadStart-style.arrow": "Flecha",
+ "arrowheadStart-style.bar": "Barra",
+ "arrowheadStart-style.diamond": "Diamante",
+ "arrowheadStart-style.dot": "Punto",
+ "arrowheadStart-style.inverted": "Invertido",
+ "arrowheadStart-style.none": "Ninguno",
+ "arrowheadStart-style.pipe": "Tubo",
+ "arrowheadStart-style.square": "Cuadrado",
+ "arrowheadStart-style.triangle": "Triángulo",
+ "color-style.black": "Negro",
+ "color-style.blue": "Azul",
+ "color-style.green": "Verde",
+ "color-style.grey": "Gris",
+ "color-style.light-blue": "Azul claro",
+ "color-style.light-green": "Verde claro",
+ "color-style.light-red": "Rojo claro",
+ "color-style.light-violet": "Violeta claro",
+ "color-style.orange": "Naranja",
+ "color-style.red": "Rojo",
+ "color-style.violet": "Violeta",
+ "color-style.yellow": "Amarillo",
+ "context-menu.arrange": "Organizar",
+ "context-menu.copy-as": "Copiar como",
+ "context-menu.export-as": "Exportar como",
+ "context-menu.move-to-page": "Mover a página",
+ "context-menu.reorder": "Reordenar",
+ "context.pages.new-page": "Nueva página",
+ "dash-style.dashed": "Discontinuo",
+ "dash-style.dotted": "Punteado",
+ "dash-style.draw": "Dibujar",
+ "dash-style.solid": "Sólido",
+ "edit-link-dialog.cancel": "Cancelar",
+ "edit-link-dialog.clear": "Borrar",
+ "edit-link-dialog.detail": "Se abrirán enlaces en una nueva pestaña.",
+ "edit-link-dialog.invalid-url": "Un enlace debe ser una URL válida.",
+ "edit-link-dialog.save": "Continuar",
+ "edit-link-dialog.title": "Editar enlace",
+ "edit-link-dialog.url": "URL",
+ "edit-pages-dialog.move-down": "Mover hacia abajo",
+ "edit-pages-dialog.move-up": "Mover hacia arriba",
+ "embed-dialog.back": "Atrás",
+ "embed-dialog.cancel": "Cancelar",
+ "embed-dialog.create": "Crear",
+ "embed-dialog.instruction": "Pegar la URL del sitio para crear la incrustación.",
+ "embed-dialog.invalid-url": "No pudimos crear una incrustación desde esa URL.",
+ "embed-dialog.title": "Crear incrustación",
+ "embed-dialog.url": "URL",
+ "file-system.confirm-open.cancel": "Cancelar",
+ "file-system.confirm-open.description": "Al abrir un archivo se sustituirá su proyecto actual y cualquier cambio no guardado se perderá. ¿Seguro que desea continuar?",
+ "file-system.confirm-open.dont-show-again": "No preguntar de nuevo",
+ "file-system.confirm-open.open": "Abrir archivo",
+ "file-system.confirm-open.title": "¿Sobrescribir proyecto actual?",
+ "file-system.file-open-error.file-format-version-too-new": "El archivo que intentó abrir es de una versión más reciente de tldraw. Vuelva a cargar la página e inténtelo de nuevo.",
+ "file-system.file-open-error.generic-corrupted-file": "El archivo que intentó abrir está corrupto.",
+ "file-system.file-open-error.not-a-tldraw-file": "El archivo que intentó abrir no parece un archivo tldraw.",
+ "file-system.file-open-error.title": "No se pudo abrir el archivo",
+ "file-system.shared-document-file-open-error.description": "No es posible abrir archivos de proyectos compartidos.",
+ "file-system.shared-document-file-open-error.title": "No se pudo abrir el archivo",
+ "fill-style.none": "Ninguno",
+ "fill-style.pattern": "Patrón",
+ "fill-style.semi": "Semi",
+ "fill-style.solid": "Sólido",
+ "focus-mode.toggle-focus-mode": "Alternar modo de enfoque",
+ "font-style.draw": "Dibujar",
+ "font-style.mono": "Mono",
+ "font-style.sans": "Sans",
+ "font-style.serif": "Serif",
+ "geo-style.arrow-down": "Flecha abajo",
+ "geo-style.arrow-left": "Flecha izquierda",
+ "geo-style.arrow-right": "Flecha derecha",
+ "geo-style.arrow-up": "Flecha arriba",
+ "geo-style.diamond": "Diamante",
+ "geo-style.ellipse": "Elipse",
+ "geo-style.hexagon": "Hexágono",
+ "geo-style.octagon": "Octógono",
+ "geo-style.oval": "Óvalo",
+ "geo-style.pentagon": "Pentágono",
+ "geo-style.rectangle": "Rectángulo",
+ "geo-style.rhombus": "Rombo",
+ "geo-style.rhombus-2": "Rombo 2",
+ "geo-style.star": "Estrella",
+ "geo-style.trapezoid": "Trapecio",
+ "geo-style.triangle": "Triángulo",
+ "geo-style.x-box": "Rectángulo con una X",
+ "help-menu.about": "Acerca de",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "Atajos de teclado",
+ "help-menu.title": "Ayuda y recursos",
+ "help-menu.twitter": "Twitter",
+ "menu.copy-as": "Copiar como",
+ "menu.edit": "Editar",
+ "menu.export-as": "Exportar como",
+ "menu.file": "Archivo",
+ "menu.language": "Idioma",
+ "menu.preferences": "Preferencias",
+ "menu.title": "Menú",
+ "menu.view": "Vista",
+ "navigation-zone.toggle-minimap": "Alternar minimapa",
+ "navigation-zone.zoom": "Zoom",
+ "opacity-style.0.1": "10 %",
+ "opacity-style.0.25": "25 %",
+ "opacity-style.0.5": "50 %",
+ "opacity-style.0.75": "75 %",
+ "opacity-style.1": "100 %",
+ "page-menu.create-new-page": "Crear nueva página",
+ "page-menu.edit-done": "Listo",
+ "page-menu.edit-start": "Editar",
+ "page-menu.max-page-count-reached": "Máx. páginas alcanzado",
+ "page-menu.new-page-initial-name": "Página 1",
+ "page-menu.submenu.delete": "Eliminar",
+ "page-menu.submenu.duplicate-page": "Duplicar",
+ "page-menu.submenu.move-down": "Mover hacia abajo",
+ "page-menu.submenu.move-up": "Mover hacia arriba",
+ "page-menu.submenu.rename": "Renombrar",
+ "page-menu.submenu.title": "Menú",
+ "page-menu.title": "Páginas",
+ "people-menu.change-color": "Cambiar color",
+ "people-menu.change-name": "Cambiar nombre",
+ "people-menu.invite": "Invitar a otros",
+ "people-menu.title": "Personas",
+ "people-menu.user": "(Usted)",
+ "share-menu.copy-link": "Copiar enlace",
+ "share-menu.copy-link-note": "Cualquier persona con el enlace podrá ver y editar este proyecto.",
+ "share-menu.copy-readonly-link": "Copiar el enlace de solo lectura",
+ "share-menu.copy-readonly-link-note": "Cualquier persona con el enlace podrá ver (pero no editar) este proyecto.",
+ "share-menu.offline-note": "Al compartir este proyecto se creará una copia en vivo alojada en una nueva URL. Puede compartir la URL hasta con treinta personas para ver y editar el proyecto conjuntamente.",
+ "share-menu.project-too-large": "Lo sentimos, este proyecto no se puede compartir porque es demasiado grande. ¡Estamos trabajando en ello!",
+ "share-menu.readonly-link": "Solo lectura",
+ "share-menu.share-project": "Compartir este proyecto",
+ "share-menu.title": "Compartir",
+ "shortcuts-dialog.edit": "Editar",
+ "shortcuts-dialog.file": "Archivo",
+ "shortcuts-dialog.preferences": "Preferencias",
+ "shortcuts-dialog.title": "Atajos de teclado",
+ "shortcuts-dialog.tools": "Herramientas",
+ "shortcuts-dialog.transform": "Transformar",
+ "shortcuts-dialog.view": "Vista",
+ "size-style.l": "Grande",
+ "size-style.m": "Medio",
+ "size-style.s": "Pequeño",
+ "size-style.xl": "Extragrande",
+ "spline-style.cubic": "Cúbico",
+ "spline-style.line": "Línea",
+ "style-panel.align": "Alinear",
+ "style-panel.arrowheads": "Puntas de flecha",
+ "style-panel.color": "Color",
+ "style-panel.dash": "Raya",
+ "style-panel.fill": "Relleno",
+ "style-panel.font": "Fuente",
+ "style-panel.geo": "Forma",
+ "style-panel.mixed": "Mixto",
+ "style-panel.opacity": "Opacidad",
+ "style-panel.size": "Tamaño",
+ "style-panel.spline": "Spline",
+ "style-panel.title": "Estilos",
+ "toast.close": "Cerrar",
+ "toast.error.copy-fail.desc": "No se pudo copiar la imagen",
+ "toast.error.copy-fail.title": "Copia fallida",
+ "toast.error.export-fail.desc": "No se pudo exportar la imagen",
+ "toast.error.export-fail.title": "Exportación fallida",
+ "tool-panel.drawing": "Dibujo",
+ "tool-panel.shapes": "Formas",
+ "tool.arrow": "Flecha",
+ "tool.arrow-down": "Flecha abajo",
+ "tool.arrow-left": "Flecha izquierda",
+ "tool.arrow-right": "Flecha derecha",
+ "tool.arrow-up": "Flecha arriba",
+ "tool.asset": "Activo",
+ "tool.diamond": "Diamante",
+ "tool.draw": "Dibujar",
+ "tool.ellipse": "Elipse",
+ "tool.embed": "Incrustación",
+ "tool.eraser": "Borrador",
+ "tool.frame": "Marco",
+ "tool.hand": "Mano",
+ "tool.hexagon": "Hexágono",
+ "tool.line": "Línea",
+ "tool.note": "Nota",
+ "tool.octagon": "Octógono",
+ "tool.oval": "Óvalo",
+ "tool.pentagon": "Pentágono",
+ "tool.rectangle": "Rectángulo",
+ "tool.rhombus": "Rombo",
+ "tool.select": "Seleccionar",
+ "tool.star": "Estrella",
+ "tool.text": "Texto",
+ "tool.trapezoid": "Trapecio",
+ "tool.triangle": "Triángulo",
+ "tool.x-box": "Rectángulo con una X",
+ "vscode.file-open.desc": "Este archivo se creó con una versión anterior de tldraw. ¿Desea actualizarlo para que funcione con la nueva versión?",
+ "vscode.file-open.dont-show-again": "No volver a preguntar"
+}
diff --git a/apps/web/public/translations/fa.json b/apps/web/public/translations/fa.json
new file mode 100644
index 00000000..4eca38d1
--- /dev/null
+++ b/apps/web/public/translations/fa.json
@@ -0,0 +1,343 @@
+{
+ "action.align-bottom": "تراز به پایین",
+ "action.align-center-horizontal": "تراز به مرکز افقی",
+ "action.align-center-horizontal.short": "تراز به مرکز افقی",
+ "action.align-center-vertical": "تراز به مرکز عمودی",
+ "action.align-center-vertical.short": "تراز به مرکز عمودی",
+ "action.align-left": "تراز به چپ",
+ "action.align-right": "تراز به راست",
+ "action.align-top": "تراز به بالا",
+ "action.back-to-content": "بازگشت به محتوا",
+ "action.bring-forward": "رو آوردن",
+ "action.bring-to-front": "آوردن به روی همه",
+ "action.convert-to-bookmark": "تبدیل به نشانه‌گذاری",
+ "action.convert-to-embed": "تبدیل به جاسازی",
+ "action.copy": "رونوشت",
+ "action.copy-as-json": "رونوشت به‌عنوان JSON",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "رونوشت به‌عنوان PNG",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "رونوشت به‌عنوان SVG",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "بریدن",
+ "action.delete": "پاک‌کردن",
+ "action.distribute-horizontal": "گسترش‌دادن افقی",
+ "action.distribute-horizontal.short": "گسترش‌دادن افقی",
+ "action.distribute-vertical": "گسترش‌دادن عمودی",
+ "action.distribute-vertical.short": "گسترش‌دادن عمودی",
+ "action.duplicate": "تکثیرکردن",
+ "action.edit-link": "ویرایش پیوند",
+ "action.exit-pen-mode": "خروج از حالت قلم",
+ "action.export-as-json": "برون‌ریزی به‌عنوان JSON",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "برون‌ریزی به‌عنوان PNG",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "برون‌ریزی به‌عنوان SVG",
+ "action.export-as-svg.short": "SVG",
+ "action.flip-horizontal": "وارونه‌سازی افقی",
+ "action.flip-horizontal.short": "وارونه‌سازی افقی",
+ "action.flip-vertical": "وارونه‌سازی عمودی",
+ "action.flip-vertical.short": "وارونه‌سازی عمودی",
+ "action.fork-project": "ایجاد انشعاب از پروژه",
+ "action.group": "گروه‌بندی",
+ "action.insert-embed": "درج جاسازی",
+ "action.insert-media": "بارگذاری رسانه",
+ "action.leave-shared-project": "ترک‌کردن پروژه هم‌رسانی‌شده",
+ "action.new-project": "پروژه جدید",
+ "action.new-shared-project": "پروژه هم‌رسانی‌شده جدید",
+ "action.open-embed-link": "بازکردن پیوند",
+ "action.open-file": "بازکردن پرونده",
+ "action.pack": "بسته‌بندی",
+ "action.paste": "جای‌گذاری",
+ "action.print": "چاپ‌کردن",
+ "action.redo": "انجام دوباره",
+ "action.rotate-ccw": "چرخاندن در خلاف جهت عقربه‌های ساعت",
+ "action.rotate-cw": "چرخاندن در جهت عقربه‌های ساعت",
+ "action.save-copy": "ذخیره یک رونوشت",
+ "action.select-all": "انتخاب همه",
+ "action.select-none": "انتخاب هیچ‌یک",
+ "action.send-backward": "پشت بردن",
+ "action.send-to-back": "بردن به پشت همه",
+ "action.share-project": "هم‌رسانی این پروژه",
+ "action.stack-horizontal": "دسته‌کردن افقی",
+ "action.stack-horizontal.short": "دسته‌کردن افقی",
+ "action.stack-vertical": "دسته‌کردن عمودی",
+ "action.stack-vertical.short": "دسته‌کردن عمودی",
+ "action.stop-following": "توقف دنبال‌کردن",
+ "action.stretch-horizontal": "کش‌آوردن افقی",
+ "action.stretch-horizontal.short": "کش‌آوردن افقی",
+ "action.stretch-vertical": "کش‌آوردن عمودی",
+ "action.stretch-vertical.short": "کش‌آوردن عمودی",
+ "action.toggle-auto-size": "تغییر وضعیت اندازه خودکار",
+ "action.toggle-dark-mode": "تغییر وضعیت حالت تاریک",
+ "action.toggle-dark-mode.menu": "حالت تاریک",
+ "action.toggle-debug-mode": "تغییر وضعیت حالت عیب‌یابی",
+ "action.toggle-debug-mode.menu": "حالت عیب‌یابی",
+ "action.toggle-focus-mode": "تغییر وضعیت حالت تمرکز",
+ "action.toggle-focus-mode.menu": "حالت تمرکز",
+ "action.toggle-grid": "تغییر وضعیت خطوط راهنما",
+ "action.toggle-grid.menu": "نمایش خطوط راهنما",
+ "action.toggle-lock": "قفل‌کردن / بازکردن قفل",
+ "action.toggle-reduce-motion": "تغییر وضعیت کاهش حرکت",
+ "action.toggle-reduce-motion.menu": "کاهش حرکت",
+ "action.toggle-snap-mode": "تغییر وضعیت حالت چسبانکی",
+ "action.toggle-snap-mode.menu": "حالت چسبانکی همیشه روشن",
+ "action.toggle-tool-lock": "تغییر وضعیت قفل ابزار",
+ "action.toggle-tool-lock.menu": "قفل ابزار",
+ "action.toggle-transparent": "تغییر وضعیت پس‌زمینه شفاف",
+ "action.toggle-transparent.context-menu": "شفاف",
+ "action.toggle-transparent.menu": "شفاف",
+ "action.undo": "واگردانی",
+ "action.ungroup": "جداکردن گروه",
+ "action.zoom-in": "بزرگ‌نمایی",
+ "action.zoom-out": "کوچک‌نمایی",
+ "action.zoom-to-100": "بزرگ‌نمایی به ۱۰۰٪",
+ "action.zoom-to-fit": "بزرگ‌نمایی به تناسب",
+ "action.zoom-to-selection": "بزرگ‌نمایی به انتخاب‌شده‌ها",
+ "actions-menu.title": "اقدامات",
+ "align-style.end": "انتها",
+ "align-style.justify": "هم‌ترازکردن",
+ "align-style.middle": "میانه",
+ "align-style.start": "شروع",
+ "arrowheadEnd-style.arrow": "پیکان",
+ "arrowheadEnd-style.bar": "مسدودشده",
+ "arrowheadEnd-style.diamond": "الماس",
+ "arrowheadEnd-style.dot": "نقطه",
+ "arrowheadEnd-style.inverted": "وارونه",
+ "arrowheadEnd-style.none": "هیچ‌یک",
+ "arrowheadEnd-style.pipe": "لوله",
+ "arrowheadEnd-style.square": "مربع",
+ "arrowheadEnd-style.triangle": "سه‌گوش",
+ "arrowheadStart-style.arrow": "پیکان",
+ "arrowheadStart-style.bar": "مسدودشده",
+ "arrowheadStart-style.diamond": "الماس",
+ "arrowheadStart-style.dot": "نقطه",
+ "arrowheadStart-style.inverted": "وارونه",
+ "arrowheadStart-style.none": "هیچ‌یک",
+ "arrowheadStart-style.pipe": "لوله",
+ "arrowheadStart-style.square": "مربع",
+ "arrowheadStart-style.triangle": "سه‌گوش",
+ "color-style.black": "مشکی",
+ "color-style.blue": "آبی",
+ "color-style.green": "سبز",
+ "color-style.grey": "خاکستری",
+ "color-style.light-blue": "آبی روشن",
+ "color-style.light-green": "سبز روشن",
+ "color-style.light-red": "قرمز روشن",
+ "color-style.light-violet": "بنفش روشن",
+ "color-style.orange": "نارنجی",
+ "color-style.red": "قرمز",
+ "color-style.violet": "بنفش",
+ "color-style.yellow": "زرد",
+ "context-menu.arrange": "مرتب‌سازی",
+ "context-menu.copy-as": "رونوشت به‌‌عنوان",
+ "context-menu.export-as": "برون‌ریزی با قالب",
+ "context-menu.move-to-page": "بردن به برگه",
+ "context-menu.reorder": "بازچینش",
+ "context.pages.new-page": "برگه جدید",
+ "dash-style.dashed": "خط‌چین",
+ "dash-style.dotted": "نقطه‌چین",
+ "dash-style.draw": "رسم",
+ "dash-style.solid": "توپُر",
+ "debug-panel.more": "بیش‌تر",
+ "edit-link-dialog.cancel": "لغو",
+ "edit-link-dialog.clear": "پاک‌سازی",
+ "edit-link-dialog.detail": "پیوندها در یک زبانه جدید باز خواهند شد.",
+ "edit-link-dialog.invalid-url": "یک پیوند باید یک نشانی اینترنتی معتبر باشد.",
+ "edit-link-dialog.save": "ادامه‌دادن",
+ "edit-link-dialog.title": "ویرایش پیوند",
+ "edit-link-dialog.url": "نشانی اینترنتی",
+ "edit-pages-dialog.move-down": "حرکت به پایین",
+ "edit-pages-dialog.move-up": "حرکت به بالا",
+ "embed-dialog.back": "بازگشت",
+ "embed-dialog.cancel": "لغو",
+ "embed-dialog.create": "ایجاد",
+ "embed-dialog.instruction": "برای ایجاد جاسازی، نشانی اینترنتی سایت را جای‌گذاری کنید.",
+ "embed-dialog.invalid-url": "نتوانستیم از آن آدرس اینترنتی، یک جاسازی ایجاد کنیم.",
+ "embed-dialog.title": "درج جاسازی",
+ "embed-dialog.url": "نشانی اینترنتی",
+ "file-system.confirm-clear.cancel": "لغو",
+ "file-system.confirm-clear.continue": "ادامه‌دادن",
+ "file-system.confirm-clear.description": "ایجاد یک پروژه جدید، پروژه کنونی شما را پاک‌سازی می‌کند و تغییرات ذخیره‌نشده از بین خواهد رفت. آیا مطمئن هستید که می‌خواهید ادامه دهید؟",
+ "file-system.confirm-clear.dont-show-again": "دوباره نپرس",
+ "file-system.confirm-clear.title": "پاک‌سازی پروژه کنونی؟",
+ "file-system.confirm-open.cancel": "لغو",
+ "file-system.confirm-open.description": "بازکردن یک پرونده جدید، آن را با پروژه کنونی شما جایگزین می‌کند و تغییرات ذخیره‌نشده از بین خواهد رفت. آیا مطمئن هستید که می‌خواهید ادامه دهید؟",
+ "file-system.confirm-open.dont-show-again": "دوباره نپرس",
+ "file-system.confirm-open.open": "بازکردن پرونده",
+ "file-system.confirm-open.title": "بازنویسی روی پروژه کنونی؟",
+ "file-system.file-open-error.file-format-version-too-new": "پرونده‌ای که تلاش کردید آن را باز کنید از یک نسخه جدیدتر tldraw است. لطفا برگه را تازه‌سازی مجدد کنید و دوباره تلاش کنید.",
+ "file-system.file-open-error.generic-corrupted-file": "پرونده‌ای که تلاش کردید آن را باز کنید خراب است.",
+ "file-system.file-open-error.not-a-tldraw-file": "پرونده‌ای که تلاش کردید آن را باز کنید شبیه یک پرونده tldraw نیست.",
+ "file-system.file-open-error.title": "پرونده باز نشد",
+ "file-system.shared-document-file-open-error.description": "بازکردن پیونده‌ها از پروژه‌های هم‌رسانی‌شده پشتیبانی نمی‌شود.",
+ "file-system.shared-document-file-open-error.title": "پرونده باز نشد",
+ "fill-style.none": "هیچ‌یک",
+ "fill-style.pattern": "الگو",
+ "fill-style.semi": "نیمه",
+ "fill-style.solid": "توپُر",
+ "focus-mode.toggle-focus-mode": "تغییر وضعیت حالت تمرکز",
+ "font-style.draw": "رسم",
+ "font-style.mono": "تک‌فاصله",
+ "font-style.sans": "سَنس",
+ "font-style.serif": "سِریف",
+ "geo-style.arrow-down": "پیکان روبه پایین",
+ "geo-style.arrow-left": "پیکان روبه چپ",
+ "geo-style.arrow-right": "پیکان روبه راست",
+ "geo-style.arrow-up": "پیکان روبه بالا",
+ "geo-style.check-box": "جعبه با تیک",
+ "geo-style.diamond": "الماس",
+ "geo-style.ellipse": "گردی",
+ "geo-style.hexagon": "شش‌ضلعی",
+ "geo-style.octagon": "هشت‌ضلعی",
+ "geo-style.oval": "تخم‌مرغی",
+ "geo-style.pentagon": "پنج‌ضلعی",
+ "geo-style.rectangle": "چهارگوش",
+ "geo-style.rhombus": "لوزی",
+ "geo-style.rhombus-2": "لوزی ۲",
+ "geo-style.star": "ستاره",
+ "geo-style.trapezoid": "ذوزنقه",
+ "geo-style.triangle": "سه‌گوش",
+ "geo-style.x-box": "جعبه با x",
+ "help-menu.about": "درباره",
+ "help-menu.discord": "دیسکورد",
+ "help-menu.github": "گیت‌هاب",
+ "help-menu.keyboard-shortcuts": "میان‌برهای صفحه‌کلید",
+ "help-menu.title": "کمک و منابع",
+ "help-menu.twitter": "توییتر",
+ "home-project-dialog.description": "این پروژه خانه محلی شماست. فقط برای شما!",
+ "home-project-dialog.ok": "باشه",
+ "home-project-dialog.title": "پروژه خانه",
+ "menu.copy-as": "رونوشت به‌‌عنوان",
+ "menu.edit": "ویرایش",
+ "menu.export-as": "برون‌ریزی با قالب",
+ "menu.file": "پرونده",
+ "menu.language": "زبان",
+ "menu.preferences": "ترجیحات",
+ "menu.title": "فهرست",
+ "menu.view": "نمایش",
+ "navigation-zone.toggle-minimap": "تغییر وضعیت نقشه کوچک",
+ "navigation-zone.zoom": "بزرگ‌نمایی",
+ "opacity-style.0.1": "۱۰٪",
+ "opacity-style.0.25": "۲۵٪",
+ "opacity-style.0.5": "۵۰٪",
+ "opacity-style.0.75": "۷۵٪",
+ "opacity-style.1": "۱۰۰٪",
+ "page-menu.create-new-page": "ایجاد برگه جدید",
+ "page-menu.edit-done": "انجام شد",
+ "page-menu.edit-start": "ویرایش",
+ "page-menu.go-to-page": "برو به برگه",
+ "page-menu.max-page-count-reached": "بیشینه تعداد برگه‌ها رسیده است",
+ "page-menu.new-page-initial-name": "برگه ۱",
+ "page-menu.submenu.delete": "پاک‌کردن",
+ "page-menu.submenu.duplicate-page": "تکثیرکردن",
+ "page-menu.submenu.move-down": "حرکت به پایین",
+ "page-menu.submenu.move-up": "حرکت به بالا",
+ "page-menu.submenu.rename": "تغییر نام",
+ "page-menu.submenu.title": "فهرست",
+ "page-menu.title": "برگه‌ها",
+ "people-menu.change-color": "تغییر رنگ",
+ "people-menu.change-name": "تغییر نام",
+ "people-menu.follow": "پی‌گرفته",
+ "people-menu.following": "پی‌گرفته",
+ "people-menu.invite": "دعوت از دیگران",
+ "people-menu.leading": "شما را پی می‌گیرد",
+ "people-menu.title": "افراد",
+ "people-menu.user": "(شما)",
+ "rename-project-dialog.cancel": "لغو",
+ "rename-project-dialog.rename": "تغییر نام",
+ "rename-project-dialog.title": "تغییر نام پروژه",
+ "share-menu.copy-link": "رونوشت پیوند هم‌رسانی",
+ "share-menu.copy-link-note": "هرکسی که این پیوند را داشته باشد می‌تواند این پروژه را ببیند و ویرایش کند.",
+ "share-menu.copy-readonly-link": "رونوشت پیوند غیرقابل ویرایش",
+ "share-menu.copy-readonly-link-note": "هرکسی که این پیوند را داشته باشد می‌تواند این پروژه را ببیند (ولی نمی‌تواند ویرایش کند).",
+ "share-menu.create-snapshot-link": "رونوشت پیوند تصویر لحظه‌ای",
+ "share-menu.default-project-name": "پروژه هم‌رسانی‌شده",
+ "share-menu.fork-note": "یک پروژه هم‌رسانی‌شده جدید براساس این تصویر لحظه‌ای ایجاد کنید.",
+ "share-menu.offline-note": "یک پروژه هم‌رسانی‌شده جدید براساس پروژه کنونی خود ایجاد کنید.",
+ "share-menu.project-too-large": "با عرض پوزش، این پروژه نمی‌تواند هم‌رسانی شود زیرا بسیار بزرگ است. ما درحال کار روی آن هستم!",
+ "share-menu.readonly-link": "غیرقابل ویرایش",
+ "share-menu.save-note": "این پروژه را در رایانه خود به عنوان پرونده .tldr بارگیری کنید.",
+ "share-menu.share-project": "هم‌رسانی این پروژه",
+ "share-menu.snapshot-link-note": "یک تصویر لحظه‌ای غیرقابل ویرایش از این پروژه ایجاد کنید و پیوند آن را هم‌رسانی کنید.",
+ "share-menu.title": "هم‌رسانی",
+ "share-menu.upload-failed": "با عرض پوزش، در این لحظه نتوانستیم پروژه شما را بارگذاری کنیم. لطفا دوباره تلاش کنید یا اگر مشکل ادامه داشت، به ما اطلاع دهید.",
+ "sharing.confirm-leave.cancel": "لغو",
+ "sharing.confirm-leave.description": "آیا مطمئن هستید که می‌خواهید این پروژه هم‌رسانی‌شده را ترک کنید؟ شما می‌توانید با رفتن به نشانی اینترنتی آن، به آن بازگردید.",
+ "sharing.confirm-leave.dont-show-again": "دوباره نپرس",
+ "sharing.confirm-leave.leave": "ترک‌کردن",
+ "sharing.confirm-leave.title": "ترک‌کردن پروژه کنونی؟",
+ "shortcuts-dialog.edit": "ویرایش",
+ "shortcuts-dialog.file": "پرونده",
+ "shortcuts-dialog.preferences": "ترجیحات",
+ "shortcuts-dialog.title": "میان‌برهای صفحه‌کلید",
+ "shortcuts-dialog.tools": "ابزارها",
+ "shortcuts-dialog.transform": "تغییر شکل",
+ "shortcuts-dialog.view": "نمایش",
+ "size-style.l": "بزرگ",
+ "size-style.m": "متوسط",
+ "size-style.s": "کوچک",
+ "size-style.xl": "خیلی بزرگ",
+ "spline-style.cubic": "مکعبی",
+ "spline-style.line": "خط",
+ "style-panel.align": "تراز",
+ "style-panel.arrowhead-end": "انتها",
+ "style-panel.arrowhead-start": "شروع",
+ "style-panel.arrowheads": "نوک‌های پیکان‌ها",
+ "style-panel.color": "رنگ",
+ "style-panel.dash": "لبه",
+ "style-panel.fill": "پُرکردن",
+ "style-panel.font": "قلم",
+ "style-panel.geo": "شکل",
+ "style-panel.mixed": "درهم",
+ "style-panel.opacity": "کدری",
+ "style-panel.position": "موقعیت",
+ "style-panel.size": "اندازه",
+ "style-panel.spline": "نوار باریک",
+ "style-panel.title": "سبک‌ها",
+ "style-panel.vertical-align": "تراز عمودی",
+ "toast.close": "بستن",
+ "toast.error.copy-fail.desc": "رونوشت تصویر با خطا مواجه شد",
+ "toast.error.copy-fail.title": "رونوشت با خطا مواجه شد",
+ "toast.error.export-fail.desc": "تصویر برون‌ریزی نشد",
+ "toast.error.export-fail.title": "برون‌ریزی ناموفق",
+ "tool-panel.drawing": "درحال رسم",
+ "tool-panel.more": "بیش‌تر",
+ "tool-panel.shapes": "شکل‌ها",
+ "tool.arrow": "پیکان",
+ "tool.arrow-down": "پیکان روبه پایین",
+ "tool.arrow-left": "پیکان روبه چپ",
+ "tool.arrow-right": "پیکان روبه راست",
+ "tool.arrow-up": "پیکان روبه بالا",
+ "tool.asset": "پیوست محتوا",
+ "tool.check-box": "جعبه با تیک",
+ "tool.diamond": "الماس",
+ "tool.draw": "رسم",
+ "tool.ellipse": "گردی",
+ "tool.embed": "جاسازی",
+ "tool.eraser": "پاک‌کن",
+ "tool.frame": "قاب",
+ "tool.hand": "دست",
+ "tool.hexagon": "شش‌ضلعی",
+ "tool.highlight": "برجسته",
+ "tool.laser": "لیزر",
+ "tool.line": "خط",
+ "tool.note": "یادداشت",
+ "tool.octagon": "هشت‌ضلعی",
+ "tool.oval": "تخم‌مرغی",
+ "tool.pentagon": "پنج‌ضلعی",
+ "tool.rectangle": "چهارگوش",
+ "tool.rhombus": "لوزی",
+ "tool.select": "انتخاب",
+ "tool.star": "ستاره",
+ "tool.text": "متن",
+ "tool.trapezoid": "ذوزنقه",
+ "tool.triangle": "سه‌گوش",
+ "tool.x-box": "جعبه با x",
+ "vscode.file-open.backup": "نسخه پشتیبان",
+ "vscode.file-open.backup-failed": "پشتیبان‌گیری انجام نشد: این یک پرونده .tldr نیست.",
+ "vscode.file-open.backup-saved": "نسخه پشتیبان ذخیره شد",
+ "vscode.file-open.desc": "ما این سند را برای کار با نسخه کنونی tldraw به‌روز کرده‌ایم. اگر می‌خواهید نسخه اصلی را نگه دارید (که روی old.tldraw.com کار می‌کند)، برای ایجاد یک نسخه پشتیبان این پایین کلیک کنید.",
+ "vscode.file-open.dont-show-again": "دوباره نپرس",
+ "vscode.file-open.open": "ادامه‌دادن"
+}
diff --git a/apps/web/public/translations/fi.json b/apps/web/public/translations/fi.json
new file mode 100644
index 00000000..f901cbc4
--- /dev/null
+++ b/apps/web/public/translations/fi.json
@@ -0,0 +1,312 @@
+{
+ "action.align-bottom": "Tasa alareuna",
+ "action.align-center-horizontal": "Tasaa pystysuunnassa",
+ "action.align-center-horizontal.short": "Tasaa P",
+ "action.align-center-vertical": "Tasaa vaakasuunnassa",
+ "action.align-center-vertical.short": "Tasaa V",
+ "action.align-left": "Tasaa vasen reuna",
+ "action.align-right": "Tasaa oikea reuna",
+ "action.align-top": "Tasaa yläreuna",
+ "action.back-to-content": "Takaisin sisältöön",
+ "action.bring-forward": "Tuo eteenpäin",
+ "action.bring-to-front": "Tuo eteen",
+ "action.convert-to-bookmark": "Muunna kirjanmerkiksi",
+ "action.convert-to-embed": "Muunna upotettavaksi",
+ "action.copy": "Kopioi",
+ "action.copy-as-json": "Kopioi JSON-muodossa",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "Kopioi PNG-muodossa",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "Kopioi SVG-muodossa",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "Leikkaa",
+ "action.delete": "Poista",
+ "action.distribute-horizontal": "Jaa pystysuunnassa",
+ "action.distribute-horizontal.short": "Jaa P",
+ "action.distribute-vertical": "Jaa vaakasuunnassa",
+ "action.distribute-vertical.short": "Jaa V",
+ "action.duplicate": "Luo kopio",
+ "action.edit-link": "Muokkaa linkkiä",
+ "action.exit-pen-mode": "Poistu kynätilasta",
+ "action.export-as-json": "Vie JSON-muodossa",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "Vie PNG-muodossa",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "Vie SVG-muodossa",
+ "action.export-as-svg.short": "SVG",
+ "action.flip-horizontal": "Käännä pystysuunnassa",
+ "action.flip-horizontal.short": "Käännä P",
+ "action.flip-vertical": "Käännä vaakasuunnassa",
+ "action.flip-vertical.short": "Käännä V",
+ "action.group": "Ryhmä",
+ "action.insert-media": "Lataa media",
+ "action.new-project": "Uusi projekti",
+ "action.new-shared-project": "Uusi jaettu projekti",
+ "action.open-embed-link": "Avaa linkki",
+ "action.open-file": "Avaa tiedosto",
+ "action.pack": "Pakkaa",
+ "action.paste": "Liitä",
+ "action.print": "Tulosta",
+ "action.redo": "Tee uudelleen",
+ "action.rotate-ccw": "Pyöritä vastapäivään",
+ "action.rotate-cw": "Pyöritä myötäpäivään",
+ "action.save-copy": "Tallenna kopio",
+ "action.select-all": "Valitse kaikki",
+ "action.select-none": "Poista valinta",
+ "action.send-backward": "Eteenpäin",
+ "action.send-to-back": "Taaksepäin",
+ "action.share-project": "Jaa tämä projekti",
+ "action.stack-horizontal": "Pinoa pystysuunnassa",
+ "action.stack-horizontal.short": "Pinoa P",
+ "action.stack-vertical": "Pinoa vaakasuunnassa",
+ "action.stack-vertical.short": "Pinoa V",
+ "action.stop-following": "Lopeta seuraaminen",
+ "action.stretch-horizontal": "Venytä pystysuunnassa",
+ "action.stretch-horizontal.short": "Venytä P",
+ "action.stretch-vertical": "Venytä vaakasuunnassa",
+ "action.stretch-vertical.short": "Venytä V",
+ "action.toggle-auto-size": "Automaattinen koko päälle/pois",
+ "action.toggle-dark-mode": "Tumma tila päälle/pois",
+ "action.toggle-dark-mode.menu": "Tumma tila",
+ "action.toggle-debug-mode": "Virheenkorjaustila päälle/pois",
+ "action.toggle-debug-mode.menu": "Virheenkorjaustila",
+ "action.toggle-focus-mode": "Keskittymistila päälle/pois",
+ "action.toggle-focus-mode.menu": "Keskittymistila",
+ "action.toggle-grid": "Ruudukko päälle/pois",
+ "action.toggle-grid.menu": "Näytä ruudukko",
+ "action.toggle-snap-mode": "Kohdista aina päälle/pois",
+ "action.toggle-snap-mode.menu": "Kohdista aina",
+ "action.toggle-tool-lock": "Työkalun lukitus päälle/pois",
+ "action.toggle-tool-lock.menu": "Työkalun lukitus",
+ "action.toggle-transparent": "Läpinäkyvä tausta päälle/pois",
+ "action.toggle-transparent.context-menu": "Läpinäkyvä",
+ "action.toggle-transparent.menu": "Läpinäkyvä",
+ "action.undo": "Peru",
+ "action.ungroup": "Pura ryhmittely",
+ "action.zoom-in": "Lähennä",
+ "action.zoom-out": "Loitonna",
+ "action.zoom-to-100": "Zoomaa 100 %:iin",
+ "action.zoom-to-fit": "Zoomaa sopivaksi",
+ "action.zoom-to-selection": "Zoomaa valintaan",
+ "actions-menu.title": "Toiminnot",
+ "align-style.end": "Loppu",
+ "align-style.justify": "Tasaa",
+ "align-style.middle": "Keskelle",
+ "align-style.start": "Alku",
+ "arrowheadEnd-style.arrow": "Nuoli",
+ "arrowheadEnd-style.bar": "Viiva",
+ "arrowheadEnd-style.diamond": "Timantti",
+ "arrowheadEnd-style.dot": "Piste",
+ "arrowheadEnd-style.inverted": "Käänteinen",
+ "arrowheadEnd-style.none": "Ei nuolenpäätä",
+ "arrowheadEnd-style.pipe": "Putki",
+ "arrowheadEnd-style.square": "Neliö",
+ "arrowheadEnd-style.triangle": "Kolmio",
+ "arrowheadStart-style.arrow": "Nuoli",
+ "arrowheadStart-style.bar": "Viiva",
+ "arrowheadStart-style.diamond": "Timantti",
+ "arrowheadStart-style.dot": "Piste",
+ "arrowheadStart-style.inverted": "Käänteinen",
+ "arrowheadStart-style.none": "Ei nuolenpäätä",
+ "arrowheadStart-style.pipe": "Putki",
+ "arrowheadStart-style.square": "Neliö",
+ "arrowheadStart-style.triangle": "Kolmio",
+ "color-style.black": "Musta",
+ "color-style.blue": "Sininen",
+ "color-style.green": "Vihreä",
+ "color-style.grey": "Harmaa",
+ "color-style.light-blue": "Vaaleansininen",
+ "color-style.light-green": "Vaaleanvihreä",
+ "color-style.light-red": "Vaalea punainen",
+ "color-style.light-violet": "Vaalea violetti",
+ "color-style.orange": "Oranssi",
+ "color-style.red": "Punainen",
+ "color-style.violet": "Violetti",
+ "color-style.yellow": "Keltainen",
+ "context-menu.arrange": "Järjestä",
+ "context-menu.copy-as": "Kopioi muodossa",
+ "context-menu.export-as": "Vie muodossa",
+ "context-menu.move-to-page": "Siirrä sivulle",
+ "context-menu.reorder": "Uudelleenjärjestä",
+ "context.pages.new-page": "Uusi sivu",
+ "dash-style.dashed": "Katkotettu",
+ "dash-style.dotted": "Pisteellinen",
+ "dash-style.draw": "Kynä",
+ "dash-style.solid": "Kiinteä",
+ "debug-panel.more": "Lisää",
+ "edit-link-dialog.cancel": "Peru",
+ "edit-link-dialog.clear": "Tyhjennä",
+ "edit-link-dialog.detail": "Linkit avautuvat uuteen välilehteen.",
+ "edit-link-dialog.invalid-url": "Linkin on oltava kelvollinen URL-osoite.",
+ "edit-link-dialog.save": "Jatka",
+ "edit-link-dialog.title": "Linkin muokkaus",
+ "edit-link-dialog.url": "Osoite",
+ "edit-pages-dialog.move-down": "Siirrä alaspäin",
+ "edit-pages-dialog.move-up": "Siirrä ylöspäin",
+ "embed-dialog.back": "Takaisin",
+ "embed-dialog.cancel": "Peru",
+ "embed-dialog.create": "Luo",
+ "embed-dialog.instruction": "Luo upotus liittämällä sivuston URL-osoite.",
+ "embed-dialog.invalid-url": "Upotettua verkko-osaa ei voitu luoda kyseisestä URL-osoitteesta.",
+ "embed-dialog.title": "Luo upotettu verkko-osa",
+ "embed-dialog.url": "Osoite",
+ "file-system.confirm-clear.cancel": "Peru",
+ "file-system.confirm-clear.continue": "Jatka",
+ "file-system.confirm-clear.description": "Uuden projektin luominen tyhjentää nykyisen projektin ja kaikki tallentamattomat muutokset menetetään. Oletko varma, että haluat jatkaa?",
+ "file-system.confirm-clear.dont-show-again": "Älä kysy uudelleen",
+ "file-system.confirm-clear.title": "Tyhjennetäänkö nykyinen projekti?",
+ "file-system.confirm-open.cancel": "Peru",
+ "file-system.confirm-open.description": "Tiedoston avaaminen korvaa nykyisen projektin ja tallentamattomat muutokset menetetään. Oletko varma, että haluat jatkaa?",
+ "file-system.confirm-open.dont-show-again": "Älä kysy uudelleen",
+ "file-system.confirm-open.open": "Avaa tiedosto",
+ "file-system.confirm-open.title": "Korvataanko nykyinen projekti?",
+ "file-system.file-open-error.file-format-version-too-new": "Tiedosto, jonka yritit avata, on uudemmasta tldraw versiosta. Ole hyvä ja yritä uudelleen sivun uudelleenlatauksen jälkeen.",
+ "file-system.file-open-error.generic-corrupted-file": "Tiedosto, jonka yritit avata, on vioittunut.",
+ "file-system.file-open-error.not-a-tldraw-file": "Tiedosto, jonka yritit avata, ei näytä tldraw-tiedostolta.",
+ "file-system.file-open-error.title": "Tiedostoa ei voitu avata",
+ "file-system.shared-document-file-open-error.description": "Tiedostojen avaaminen jaetuista projekteista ei ole tuettu.",
+ "file-system.shared-document-file-open-error.title": "Tiedostoa ei voitu avata",
+ "fill-style.none": "Ei täyttöä",
+ "fill-style.pattern": "Kuvio",
+ "fill-style.semi": "Osittainen",
+ "fill-style.solid": "Kiinteä",
+ "focus-mode.toggle-focus-mode": "Keskittymistila päälle/pois",
+ "font-style.draw": "Kynä",
+ "font-style.mono": "Mono",
+ "font-style.sans": "Sans",
+ "font-style.serif": "Serif",
+ "geo-style.arrow-down": "Alanuoli",
+ "geo-style.arrow-left": "Vasen nuoli",
+ "geo-style.arrow-right": "Oikea nuoli",
+ "geo-style.arrow-up": "Ylänuoli",
+ "geo-style.diamond": "Timantti",
+ "geo-style.ellipse": "Soikio",
+ "geo-style.hexagon": "Kuusikulmio",
+ "geo-style.octagon": "Kahdeksankulmio",
+ "geo-style.oval": "Soikio",
+ "geo-style.pentagon": "Viisikulmio",
+ "geo-style.rectangle": "Suorakulmio",
+ "geo-style.rhombus": "Neljäkäs",
+ "geo-style.rhombus-2": "Neljäkäs 2",
+ "geo-style.star": "Tähti",
+ "geo-style.trapezoid": "Puolisuunnikas",
+ "geo-style.triangle": "Kolmio",
+ "geo-style.x-box": "X laatikko",
+ "help-menu.about": "Tietoja",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "Pikanäppäimet",
+ "help-menu.title": "Ohje ja resurssit",
+ "help-menu.twitter": "Twitter",
+ "menu.copy-as": "Kopioi muodossa",
+ "menu.edit": "Muokkaa",
+ "menu.export-as": "Vie muodossa",
+ "menu.file": "Tiedosto",
+ "menu.language": "Kieli",
+ "menu.preferences": "Asetukset",
+ "menu.title": "Valikko",
+ "menu.view": "Näytä",
+ "navigation-zone.toggle-minimap": "Vaihda minikartta päälle/pois",
+ "navigation-zone.zoom": "Zoomaa",
+ "opacity-style.0.1": "10%",
+ "opacity-style.0.25": "25%",
+ "opacity-style.0.5": "50%",
+ "opacity-style.0.75": "75%",
+ "opacity-style.1": "100%",
+ "page-menu.create-new-page": "Luo uusi sivu",
+ "page-menu.edit-done": "Valmis",
+ "page-menu.edit-start": "Muokkaa",
+ "page-menu.max-page-count-reached": "Maksimi sivumäärä saavutettu",
+ "page-menu.new-page-initial-name": "Sivu 1",
+ "page-menu.submenu.delete": "Poista",
+ "page-menu.submenu.duplicate-page": "Luo kopio",
+ "page-menu.submenu.move-down": "Siirrä alaspäin",
+ "page-menu.submenu.move-up": "Siirrä ylöspäin",
+ "page-menu.submenu.rename": "Nimeä uudelleen",
+ "page-menu.submenu.title": "Valikko",
+ "page-menu.title": "Sivut",
+ "people-menu.change-color": "Muuta väri",
+ "people-menu.change-name": "Muuta nimi",
+ "people-menu.follow": "Seuraa",
+ "people-menu.invite": "Lähetä kutsu",
+ "people-menu.title": "Ihmiset",
+ "people-menu.user": "(Sinä)",
+ "share-menu.copy-link": "Kopioi linkki",
+ "share-menu.copy-link-note": "Kuka tahansa, jolla on linkki, voi tarkastella ja muokata tätä projektia.",
+ "share-menu.copy-readonly-link": "Kopioi vain lukuoikeuslinkki",
+ "share-menu.copy-readonly-link-note": "Kuka tahansa, jolla on linkki, voi tarkastella (mutta ei muokata) tätä projektia.",
+ "share-menu.offline-note": "Tämän projektin jakaminen luo live-kopion uuteen URL-osoitteeseen. Voit jakaa osoitteen enintään kolmenkymmenen muun henkilön kanssa, jotta he voivat tarkastella ja muokata projektia yhdessä.",
+ "share-menu.project-too-large": "Valitettavasti tätä projektia ei voi jakaa, koska se on liian suuri. Me työstämme sitä!",
+ "share-menu.readonly-link": "Vain luku",
+ "share-menu.share-project": "Jaa tämä projekti",
+ "share-menu.title": "Jaa",
+ "shortcuts-dialog.edit": "Muokkaa",
+ "shortcuts-dialog.file": "Tiedosto",
+ "shortcuts-dialog.preferences": "Asetukset",
+ "shortcuts-dialog.title": "Pikanäppäimet",
+ "shortcuts-dialog.tools": "Työkalut",
+ "shortcuts-dialog.transform": "Muunna",
+ "shortcuts-dialog.view": "Näytä",
+ "size-style.l": "Suuri",
+ "size-style.m": "Keskikokoinen",
+ "size-style.s": "Pieni",
+ "size-style.xl": "Erittäin suuri",
+ "spline-style.cubic": "Kuutio",
+ "spline-style.line": "Viiva",
+ "style-panel.align": "Tasaa",
+ "style-panel.arrowhead-end": "Pääty",
+ "style-panel.arrowhead-start": "Alku",
+ "style-panel.arrowheads": "Nuolenpäät",
+ "style-panel.color": "Väri",
+ "style-panel.dash": "Ääriviiva",
+ "style-panel.fill": "Täyttö",
+ "style-panel.font": "Fonttiperhe",
+ "style-panel.geo": "Muoto",
+ "style-panel.mixed": "Sekalainen",
+ "style-panel.opacity": "Peittävyys",
+ "style-panel.position": "Sijainti",
+ "style-panel.size": "Koko",
+ "style-panel.spline": "Käyrä",
+ "style-panel.title": "Tyylit",
+ "toast.close": "Sulje",
+ "toast.error.copy-fail.desc": "Kuvan kopiointi epäonnistui",
+ "toast.error.copy-fail.title": "Kopiointi epäonnistui",
+ "toast.error.export-fail.desc": "Kuvan vienti epäonnistui",
+ "toast.error.export-fail.title": "Vienti epäonnistui",
+ "tool-panel.drawing": "Kynä",
+ "tool-panel.more": "Lisää",
+ "tool-panel.shapes": "Muodot",
+ "tool.arrow": "Nuoli",
+ "tool.arrow-down": "Alanuoli",
+ "tool.arrow-left": "Vasen nuoli",
+ "tool.arrow-right": "Oikea nuoli",
+ "tool.arrow-up": "Ylänuoli",
+ "tool.asset": "Liite",
+ "tool.diamond": "Timantti",
+ "tool.draw": "Kynä",
+ "tool.ellipse": "Soikio",
+ "tool.embed": "Upota",
+ "tool.eraser": "Pyyhin",
+ "tool.frame": "Kehys",
+ "tool.hand": "Siirrä",
+ "tool.hexagon": "Kuusikulmio",
+ "tool.line": "Viiva",
+ "tool.note": "Muistilappu",
+ "tool.octagon": "Kahdeksankulmio",
+ "tool.oval": "Soikio",
+ "tool.pentagon": "Viisikulmio",
+ "tool.rectangle": "Suorakulmio",
+ "tool.rhombus": "Nelikulmio",
+ "tool.select": "Valitse",
+ "tool.star": "Tähti",
+ "tool.text": "Teksti",
+ "tool.trapezoid": "Puolisuunnikas",
+ "tool.triangle": "Kolmio",
+ "tool.x-box": "X laatikko",
+ "vscode.file-open.backup": "Varmuuskopio",
+ "vscode.file-open.backup-failed": "Varmuuskopiointi epäonnistui: tämä ei ole .tldr-tiedosto.",
+ "vscode.file-open.backup-saved": "Varmuuskopio tallennettu",
+ "vscode.file-open.desc": "Tämä tiedosto on luoto aiemmalla tldraw versiolla. Haluatko päivittää sen toimimaan uuden version kanssa?",
+ "vscode.file-open.dont-show-again": "Älä kysy uudelleen",
+ "vscode.file-open.open": "Jatka"
+}
diff --git a/apps/web/public/translations/fr.json b/apps/web/public/translations/fr.json
new file mode 100644
index 00000000..7c5213ed
--- /dev/null
+++ b/apps/web/public/translations/fr.json
@@ -0,0 +1,372 @@
+{
+ "action.align-bottom": "Aligner en bas",
+ "action.align-center-horizontal": "Aligner horizontalement",
+ "action.align-center-horizontal.short": "Aligner H",
+ "action.align-center-vertical": "Aligner verticalement",
+ "action.align-center-vertical.short": "Aligner V",
+ "action.align-left": "Aligner à gauche",
+ "action.align-right": "Aligner à droite",
+ "action.align-top": "Aligner en haut",
+ "action.back-to-content": "Retour au contenu",
+ "action.bring-forward": "Mettre en avant",
+ "action.bring-to-front": "Mettre au premier plan",
+ "action.convert-to-bookmark": "Convertir en signet",
+ "action.convert-to-embed": "Convertir en intégration",
+ "action.copy": "Copier",
+ "action.copy-as-json": "Copier en tant que JSON",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "Copier en tant que PNG",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "Copier en tant que SVG",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "Couper",
+ "action.delete": "Supprimer",
+ "action.distribute-horizontal": "Distribuer horizontalement",
+ "action.distribute-horizontal.short": "Distribuer H",
+ "action.distribute-vertical": "Distribuer verticalement",
+ "action.distribute-vertical.short": "Distribuer V",
+ "action.duplicate": "Dupliquer",
+ "action.edit-link": "Modifier le lien",
+ "action.exit-pen-mode": "Quitter le mode stylet",
+ "action.export-all-as-json": "Exporter tout en tant que JSON",
+ "action.export-all-as-json.short": "JSON",
+ "action.export-all-as-png": "Exporter tout en tant que PNG",
+ "action.export-all-as-png.short": "PNG",
+ "action.export-all-as-svg": "Exporter tout en tant que SVG",
+ "action.export-all-as-svg.short": "SVG",
+ "action.export-as-json": "Exporter en tant que JSON",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "Exporter en tant que PNG",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "Exporter en tant que SVG",
+ "action.export-as-svg.short": "SVG",
+ "action.fit-frame-to-content": "Ajuster au contenu",
+ "action.flip-horizontal": "Retourner horizontalement",
+ "action.flip-horizontal.short": "Retourner H",
+ "action.flip-vertical": "Retourner verticalement",
+ "action.flip-vertical.short": "Retourner V",
+ "action.fork-project": "Dupliquer ce projet",
+ "action.group": "Grouper",
+ "action.insert-embed": "Insérer l'intégration",
+ "action.insert-media": "Charger un média",
+ "action.leave-shared-project": "Quitter le projet partagé",
+ "action.new-project": "Nouveau projet",
+ "action.new-shared-project": "Nouveau projet partagé",
+ "action.open-cursor-chat": "Curseur du chat",
+ "action.open-embed-link": "Ouvrir le lien",
+ "action.open-file": "Ouvrir le fichier",
+ "action.pack": "Paquet",
+ "action.paste": "Coller",
+ "action.print": "Imprimer",
+ "action.redo": "Rétablir",
+ "action.remove-frame": "Supprimer le cadre",
+ "action.rename": "Renommer",
+ "action.rotate-ccw": "Tourner dans le sens inverse des aiguilles d'une montre",
+ "action.rotate-cw": "Tourner dans le sens des aiguilles d'une montre",
+ "action.save-copy": "Enregistrer une copie",
+ "action.select-all": "Sélectionner tout",
+ "action.select-none": "Sélectionner aucun",
+ "action.send-backward": "Envoyer vers l'arrière",
+ "action.send-to-back": "Envoyer à l'arrière",
+ "action.share-project": "Partager ce projet",
+ "action.stack-horizontal": "Empiler horizontalement",
+ "action.stack-horizontal.short": "Empiler H",
+ "action.stack-vertical": "Empiler verticalement",
+ "action.stack-vertical.short": "Empiler V",
+ "action.stop-following": "Ne plus suivre",
+ "action.stretch-horizontal": "Étirer horizontalement",
+ "action.stretch-horizontal.short": "Étirer H",
+ "action.stretch-vertical": "Étirer verticalement",
+ "action.stretch-vertical.short": "Étirer V",
+ "action.toggle-auto-size": "Activer le dimensionnement automatique",
+ "action.toggle-dark-mode": "Activer le mode sombre",
+ "action.toggle-dark-mode.menu": "Mode sombre",
+ "action.toggle-debug-mode": "Activer le mode débogage",
+ "action.toggle-debug-mode.menu": "Mode débogage",
+ "action.toggle-edge-scrolling": "Activer le défilement des bords",
+ "action.toggle-edge-scrolling.menu": "Défilement des bords",
+ "action.toggle-focus-mode": "Activer le mode Focus",
+ "action.toggle-focus-mode.menu": "Mode Focus",
+ "action.toggle-grid": "Activer la grille",
+ "action.toggle-grid.menu": "Afficher la grille",
+ "action.toggle-lock": "Verrouiller / Déverrouiller",
+ "action.toggle-reduce-motion": "Activer la réduction des mouvements",
+ "action.toggle-reduce-motion.menu": "Réduire les mouvements",
+ "action.toggle-snap-mode": "Activer la fonction Toujours aligner",
+ "action.toggle-snap-mode.menu": "Toujours aligner",
+ "action.toggle-tool-lock": "Activer le verrouillage de l'outil",
+ "action.toggle-tool-lock.menu": "Verrouillage de l'outil",
+ "action.toggle-transparent": "Activer l'arrière-plan transparent",
+ "action.toggle-transparent.context-menu": "Transparent",
+ "action.toggle-transparent.menu": "Transparent",
+ "action.toggle-wrap-mode": "Activer la sélection au retour à la ligne.",
+ "action.toggle-wrap-mode.menu": "Sélectionner au retour à la ligne",
+ "action.undo": "Annuler",
+ "action.ungroup": "Dissocier",
+ "action.unlock-all": "Tout déverrouiller",
+ "action.zoom-in": "Zoomer",
+ "action.zoom-out": "Dézoomer",
+ "action.zoom-to-100": "Zoomer à 100%",
+ "action.zoom-to-fit": "Zoomer pour ajuster",
+ "action.zoom-to-selection": "Zoomer sur la sélection",
+ "actions-menu.title": "Actions",
+ "align-style.end": "Fin",
+ "align-style.justify": "Justifier",
+ "align-style.middle": "Milieu",
+ "align-style.start": "Début",
+ "arrowheadEnd-style.arrow": "Flèche",
+ "arrowheadEnd-style.bar": "Barre",
+ "arrowheadEnd-style.diamond": "Diamant",
+ "arrowheadEnd-style.dot": "Point",
+ "arrowheadEnd-style.inverted": "Inversé",
+ "arrowheadEnd-style.none": "Aucun",
+ "arrowheadEnd-style.pipe": "Tuyau",
+ "arrowheadEnd-style.square": "Carré",
+ "arrowheadEnd-style.triangle": "Triangle",
+ "arrowheadStart-style.arrow": "Flèche",
+ "arrowheadStart-style.bar": "Barre",
+ "arrowheadStart-style.diamond": "Diamant",
+ "arrowheadStart-style.dot": "Point",
+ "arrowheadStart-style.inverted": "Inversé",
+ "arrowheadStart-style.none": "Aucun",
+ "arrowheadStart-style.pipe": "Tuyau",
+ "arrowheadStart-style.square": "Carré",
+ "arrowheadStart-style.triangle": "Triangle",
+ "assets.files.upload-failed": "Échec du téléchargement",
+ "assets.url.failed": "Impossible de charger l'aperçu de l'URL",
+ "color-style.black": "Noir",
+ "color-style.blue": "Bleu",
+ "color-style.green": "Vert",
+ "color-style.grey": "Gris",
+ "color-style.light-blue": "Bleu clair",
+ "color-style.light-green": "Vert clair",
+ "color-style.light-red": "Rouge clair",
+ "color-style.light-violet": "Violet clair",
+ "color-style.orange": "Orange",
+ "color-style.red": "Rouge",
+ "color-style.violet": "Violet",
+ "color-style.white": "Blanc",
+ "color-style.yellow": "Jaune",
+ "context-menu.arrange": "Organiser",
+ "context-menu.copy-as": "Copier en tant que",
+ "context-menu.export-all-as": "Exporter tout en tant que",
+ "context-menu.export-as": "Exporter en tant que",
+ "context-menu.move-to-page": "Déplacer vers la page",
+ "context-menu.reorder": "Réorganiser",
+ "context.pages.new-page": "Nouvelle page",
+ "cursor-chat.type-to-chat": "Tapez pour discuter...",
+ "dash-style.dashed": "Discontinu",
+ "dash-style.dotted": "Pointillé",
+ "dash-style.draw": "Dessiner",
+ "dash-style.solid": "Solide",
+ "debug-panel.more": "Plus",
+ "document.default-name": "Sans titre",
+ "edit-link-dialog.cancel": "Annuler",
+ "edit-link-dialog.clear": "Effacer",
+ "edit-link-dialog.detail": "Les liens s'ouvriront dans un nouvel onglet.",
+ "edit-link-dialog.invalid-url": "Un lien doit être une URL valide.",
+ "edit-link-dialog.save": "Continuer",
+ "edit-link-dialog.title": "Modifier le lien",
+ "edit-link-dialog.url": "URL",
+ "edit-pages-dialog.move-down": "Déplacer vers le bas",
+ "edit-pages-dialog.move-up": "Déplacer vers le haut",
+ "embed-dialog.back": "Retour",
+ "embed-dialog.cancel": "Annuler",
+ "embed-dialog.create": "Créer",
+ "embed-dialog.instruction": "Collez l'URL du site pour créer l'intégration.",
+ "embed-dialog.invalid-url": "Nous n'avons pas pu créer une intégration (embed) à partir de cette URL.",
+ "embed-dialog.title": "Créer une intégration",
+ "embed-dialog.url": "URL",
+ "file-system.confirm-clear.cancel": "Annuler",
+ "file-system.confirm-clear.continue": "Continuer",
+ "file-system.confirm-clear.description": "Créer un nouveau projet effacera l'actuel et toutes modifications non sauvegardées seront perdues. Vous êtes sûrs de vouloir continuer ?",
+ "file-system.confirm-clear.dont-show-again": "Ne plus demander",
+ "file-system.confirm-clear.title": "Effacer le projet en cours ?",
+ "file-system.confirm-open.cancel": "Annuler",
+ "file-system.confirm-open.description": "L'ouverture d'un fichier remplacera votre projet actuel et toutes les modifications non enregistrées seront perdues. Voulez-vous vraiment continuer ?",
+ "file-system.confirm-open.dont-show-again": "Ne plus demander",
+ "file-system.confirm-open.open": "Ouvrir le fichier",
+ "file-system.confirm-open.title": "Remplacer le projet actuel?",
+ "file-system.file-open-error.file-format-version-too-new": "Le fichier que vous avez tenté d'ouvrir provient d'une version plus récente de tldraw. Veuillez recharger la page et réessayer.",
+ "file-system.file-open-error.generic-corrupted-file": "Le fichier que vous avez tenté d'ouvrir est corrompu.",
+ "file-system.file-open-error.not-a-tldraw-file": "Le fichier que vous avez tenté d'ouvrir ne ressemble pas à un fichier tldraw.",
+ "file-system.file-open-error.title": "Ouverture du fichier impossible",
+ "file-system.shared-document-file-open-error.description": "L'ouverture de fichiers à partir de projets partagés n'est pas prise en charge.",
+ "file-system.shared-document-file-open-error.title": "Impossible d'ouvrir le fichier",
+ "fill-style.none": "Aucun",
+ "fill-style.pattern": "Motif",
+ "fill-style.semi": "Semi",
+ "fill-style.solid": "Solide",
+ "focus-mode.toggle-focus-mode": "Basculer le mode Focus",
+ "font-style.draw": "Dessiner",
+ "font-style.mono": "Mono",
+ "font-style.sans": "Sans",
+ "font-style.serif": "Serif",
+ "geo-style.arrow-down": "Flèche vers le bas",
+ "geo-style.arrow-left": "Flèche vers la gauche",
+ "geo-style.arrow-right": "Flèche vers la droite",
+ "geo-style.arrow-up": "Flèche vers le haut",
+ "geo-style.check-box": "Case à cocher",
+ "geo-style.cloud": "Nuage",
+ "geo-style.diamond": "Diamant",
+ "geo-style.ellipse": "Ellipse",
+ "geo-style.hexagon": "Hexagone",
+ "geo-style.octagon": "Octogone",
+ "geo-style.oval": "Ovale",
+ "geo-style.pentagon": "Pentagone",
+ "geo-style.rectangle": "Rectangle",
+ "geo-style.rhombus": "Losange",
+ "geo-style.rhombus-2": "Losange 2",
+ "geo-style.star": "Étoile",
+ "geo-style.trapezoid": "Trapèze",
+ "geo-style.triangle": "Triangle",
+ "geo-style.x-box": "X Box",
+ "help-menu.about": "À propos",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "Raccourcis clavier",
+ "help-menu.title": "Aide et ressources",
+ "help-menu.twitter": "Twitter",
+ "home-project-dialog.description": "Ceci est votre projet local personnel. Il vous est exclusif!",
+ "home-project-dialog.ok": "Ok",
+ "home-project-dialog.title": "Projet d'accueil",
+ "menu.copy-as": "Copier en tant que",
+ "menu.edit": "Modifier",
+ "menu.export-as": "Exporter en tant que",
+ "menu.file": "Fichier",
+ "menu.language": "Langue",
+ "menu.preferences": "Préférences",
+ "menu.title": "Menu",
+ "menu.view": "Vue",
+ "navigation-zone.toggle-minimap": "Basculer la mini-carte",
+ "navigation-zone.zoom": "Zoomer",
+ "opacity-style.0.1": "10 %",
+ "opacity-style.0.25": "25%",
+ "opacity-style.0.5": "50%",
+ "opacity-style.0.75": "75%",
+ "opacity-style.1": "100%",
+ "page-menu.create-new-page": "Créer une nouvelle page",
+ "page-menu.edit-done": "Terminé",
+ "page-menu.edit-start": "Modifier",
+ "page-menu.go-to-page": "Aller à la page",
+ "page-menu.max-page-count-reached": "Nombre maximal de pages atteint",
+ "page-menu.new-page-initial-name": "Page 1",
+ "page-menu.submenu.delete": "Supprimer",
+ "page-menu.submenu.duplicate-page": "Dupliquer",
+ "page-menu.submenu.move-down": "Déplacer vers le bas",
+ "page-menu.submenu.move-up": "Déplacer vers le haut",
+ "page-menu.submenu.rename": "Renommer",
+ "page-menu.submenu.title": "Menu",
+ "page-menu.title": "Pages",
+ "people-menu.change-color": "Modifier la couleur",
+ "people-menu.change-name": "Modifier le nom",
+ "people-menu.follow": "Suivre",
+ "people-menu.following": "Suivis",
+ "people-menu.invite": "Inviter d'autres personnes",
+ "people-menu.leading": "Vous suivant",
+ "people-menu.title": "Personnes",
+ "people-menu.user": "(Vous)",
+ "rename-project-dialog.cancel": "Annuler",
+ "rename-project-dialog.rename": "Renommer",
+ "rename-project-dialog.title": "Renommer le projet",
+ "share-menu.copy-link": "Copier le lien",
+ "share-menu.copy-link-note": "Toute personne disposant du lien peut lire et modifier ce projet.",
+ "share-menu.copy-readonly-link": "Copier le lien en lecture seule",
+ "share-menu.copy-readonly-link-note": "Toute personne disposant du lien peut voir (mais pas modifier) ce projet.",
+ "share-menu.create-snapshot-link": "Créer un lien instantané",
+ "share-menu.default-project-name": "Projet partagé",
+ "share-menu.fork-note": "Créer un nouveau projet partagé basé sur ce lien instantané.",
+ "share-menu.offline-note": "Le partage de ce projet créera une copie réelle hébergée sur une nouvelle URL. Vous pourrez partager l'URL avec un maximum de trente personnes pour afficher et modifier le projet ensemble.",
+ "share-menu.project-too-large": "Désolé, il n'est pas possible de partager ce projet, car il est trop volumineux. Nous travaillons à la résolution de ce problème!",
+ "share-menu.readonly-link": "Lecture seule",
+ "share-menu.save-note": "Téléchargez localement ce projet en tant que fichier .tldr",
+ "share-menu.share-project": "Partager ce projet",
+ "share-menu.snapshot-link-note": "Capturez et partagez ce projet sous forme de lien d'instantané en lecture seule.",
+ "share-menu.title": "Partager",
+ "share-menu.upload-failed": "Nous sommes désolés, votre projet ne peut être importé pour le moment. Merci de réessayer ou nous en informer si le problème persiste.",
+ "sharing.confirm-leave.cancel": "Annuler",
+ "sharing.confirm-leave.description": "Êtes-vous sûrs de vouloir quitter ce projet partagé ? Vous pourrez y retourner en accédant à son URL.",
+ "sharing.confirm-leave.dont-show-again": "Ne plus demander",
+ "sharing.confirm-leave.leave": "Quitter",
+ "sharing.confirm-leave.title": "Quitter le projet actuel",
+ "shortcuts-dialog.collaboration": "Collaboration",
+ "shortcuts-dialog.edit": "Modifier",
+ "shortcuts-dialog.file": "Fichier",
+ "shortcuts-dialog.preferences": "Préférences",
+ "shortcuts-dialog.title": "Raccourcis clavier",
+ "shortcuts-dialog.tools": "Outils",
+ "shortcuts-dialog.transform": "Transformer",
+ "shortcuts-dialog.view": "Vue",
+ "size-style.l": "Large",
+ "size-style.m": "Moyen",
+ "size-style.s": "Petit",
+ "size-style.xl": "Extra-large",
+ "spline-style.cubic": "Cubique",
+ "spline-style.line": "Ligne",
+ "status.offline": "Hors ligne",
+ "status.online": "En ligne",
+ "style-panel.align": "Alignement",
+ "style-panel.arrowhead-end": "Fin",
+ "style-panel.arrowhead-start": "Début",
+ "style-panel.arrowheads": "Pointes de flèches",
+ "style-panel.color": "Couleur",
+ "style-panel.dash": "Tiret",
+ "style-panel.fill": "Remplir",
+ "style-panel.font": "Police",
+ "style-panel.geo": "Forme",
+ "style-panel.mixed": "Mélangé",
+ "style-panel.opacity": "Opacité",
+ "style-panel.position": "Position",
+ "style-panel.size": "Taille",
+ "style-panel.spline": "Spline",
+ "style-panel.title": "Styles",
+ "style-panel.vertical-align": "Alignement vertical",
+ "toast.close": "Fermer",
+ "toast.error.copy-fail.desc": "Échec de la copie de l'image",
+ "toast.error.copy-fail.title": "Échec de la copie",
+ "toast.error.export-fail.desc": "Échec de l'exportation de l'image",
+ "toast.error.export-fail.title": "Échec de l'exportation",
+ "tool-panel.drawing": "Dessin",
+ "tool-panel.more": "Plus",
+ "tool-panel.shapes": "Formes",
+ "tool.arrow": "Flèche",
+ "tool.arrow-down": "Flèche vers le bas",
+ "tool.arrow-left": "Flèche vers la gauche",
+ "tool.arrow-right": "Flèche vers la droite",
+ "tool.arrow-up": "Flèche vers le haut",
+ "tool.asset": "Actif",
+ "tool.check-box": "Case à cocher",
+ "tool.cloud": "Nuage",
+ "tool.diamond": "Diamant",
+ "tool.draw": "Dessiner",
+ "tool.ellipse": "Ellipse",
+ "tool.embed": "Intégration",
+ "tool.eraser": "Gomme",
+ "tool.frame": "Cadre",
+ "tool.hand": "Main",
+ "tool.hexagon": "Hexagone",
+ "tool.highlight": "Surligner",
+ "tool.laser": "Laser",
+ "tool.line": "Ligne",
+ "tool.note": "Note",
+ "tool.octagon": "Octogone",
+ "tool.oval": "Ovale",
+ "tool.pentagon": "Pentagone",
+ "tool.rectangle": "Rectangle",
+ "tool.rhombus": "Losange",
+ "tool.select": "Sélection",
+ "tool.star": "Étoile",
+ "tool.text": "Texte",
+ "tool.trapezoid": "Trapèze",
+ "tool.triangle": "Triangle",
+ "tool.x-box": "X box",
+ "verticalAlign-style.end": "Bas",
+ "verticalAlign-style.middle": "Milieu",
+ "verticalAlign-style.start": "Haut",
+ "vscode.file-open.backup": "Sauvegarde",
+ "vscode.file-open.backup-failed": "Échec de la sauvegarde : ceci n'est pas un fichier .tldr",
+ "vscode.file-open.backup-saved": "Sauvegarde enregistrée",
+ "vscode.file-open.desc": "Ce fichier a été créé avec une version antérieure de tldraw. Souhaitez-vous le mettre à jour pour qu’il fonctionne avec la nouvelle version ?",
+ "vscode.file-open.dont-show-again": "Ne plus demander",
+ "vscode.file-open.open": "Continuer"
+}
diff --git a/apps/web/public/translations/gl.json b/apps/web/public/translations/gl.json
new file mode 100644
index 00000000..460fcf2c
--- /dev/null
+++ b/apps/web/public/translations/gl.json
@@ -0,0 +1,349 @@
+{
+ "action.align-bottom": "Aliñar abaixo",
+ "action.align-center-horizontal": "Aliñar ao centro horizontalmente",
+ "action.align-center-horizontal.short": "Aliñar ao centro horizontalmente",
+ "action.align-center-vertical": "Aliñar ao centro verticalmente",
+ "action.align-center-vertical.short": "Aliñar ao centro verticalmente",
+ "action.align-left": "Aliñar á esquerda",
+ "action.align-right": "Aliñar á dereita",
+ "action.align-top": "Aliñar arriba",
+ "action.back-to-content": "Volver ao contido",
+ "action.bring-forward": "Mover adiante",
+ "action.bring-to-front": "Mover ao fronte",
+ "action.convert-to-bookmark": "Converter a favorito",
+ "action.convert-to-embed": "Converter a embed",
+ "action.copy": "Copiar",
+ "action.copy-as-json": "Copiar como JSON",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "Copiar como PNG",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "Copiar como SVG",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "Cortar",
+ "action.delete": "Borrar",
+ "action.distribute-horizontal": "Distribuír horizontalmente",
+ "action.distribute-horizontal.short": "Distribuír horizontalmente",
+ "action.distribute-vertical": "Distribuír verticalmente",
+ "action.distribute-vertical.short": "Distribuír verticalmente",
+ "action.duplicate": "Duplicar",
+ "action.edit-link": "Editar ligazón",
+ "action.exit-pen-mode": "Saír do modo pluma",
+ "action.export-as-json": "Exportar como JSON",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "Exportar como PNG",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "Exportar como SVG",
+ "action.export-as-svg.short": "SVG",
+ "action.flip-horizontal": "Voltear horizontalmente",
+ "action.flip-horizontal.short": "Voltear horizontalmente",
+ "action.flip-vertical": "Voltear verticalmente",
+ "action.flip-vertical.short": "Voltear verticalmente",
+ "action.fork-project": "Copiar este proxecto",
+ "action.group": "Agrupar",
+ "action.insert-embed": "Inserir embed",
+ "action.insert-media": "Subir medios",
+ "action.leave-shared-project": "Saír do proxecto compartido",
+ "action.new-project": "Novo proxecto",
+ "action.new-shared-project": "Novo proxecto compartido",
+ "action.open-cursor-chat": "Conversa de cursor",
+ "action.open-embed-link": "Abrir ligazón",
+ "action.open-file": "Abrir arquivo",
+ "action.pack": "Empaquetar",
+ "action.paste": "Pegar",
+ "action.print": "Imprimir",
+ "action.redo": "Refacer",
+ "action.rotate-ccw": "Rotar en sentido antihorario",
+ "action.rotate-cw": "Rotar en sentido horario",
+ "action.save-copy": "Gardar unha copia",
+ "action.select-all": "Selecionar todo",
+ "action.select-none": "Selecionar nada",
+ "action.send-backward": "Mover atrás",
+ "action.send-to-back": "Mover ao fondo",
+ "action.share-project": "Compartir este proxecto",
+ "action.stack-horizontal": "Amorear Horizontalmente",
+ "action.stack-horizontal.short": "Amorear H",
+ "action.stack-vertical": "Amorear Verticalmente",
+ "action.stack-vertical.short": "Amorear V",
+ "action.stop-following": "Deixar de seguir",
+ "action.stretch-horizontal": "Estirar horizontalmente",
+ "action.stretch-horizontal.short": "Estirar horizontalmente",
+ "action.stretch-vertical": "Estirar verticalmente",
+ "action.stretch-vertical.short": "Estirar verticalmente",
+ "action.toggle-auto-size": "Alternar tamaño automático",
+ "action.toggle-dark-mode": "Modo escuro",
+ "action.toggle-dark-mode.menu": "Modo escuro",
+ "action.toggle-debug-mode": "Modo depuración",
+ "action.toggle-debug-mode.menu": "Modo depuración",
+ "action.toggle-focus-mode": "Modo concentración",
+ "action.toggle-focus-mode.menu": "Modo concentración",
+ "action.toggle-grid": "Amosar cuadrícula",
+ "action.toggle-grid.menu": "Amosar cuadrícula",
+ "action.toggle-lock": "Bloqueo / Desbloqueo",
+ "action.toggle-reduce-motion": "Alternar redución de movemento",
+ "action.toggle-reduce-motion.menu": "Reducir movemento",
+ "action.toggle-snap-mode": "Amosar puntos de axuste",
+ "action.toggle-snap-mode.menu": "Amosar puntos de axuste",
+ "action.toggle-tool-lock": "Alternar bloqueo de ferramentas",
+ "action.toggle-tool-lock.menu": "Bloqueo de ferramentas",
+ "action.toggle-transparent": "Alternar fondo transparente",
+ "action.toggle-transparent.context-menu": "Transparente",
+ "action.toggle-transparent.menu": "Transparente",
+ "action.undo": "Desfacer",
+ "action.ungroup": "Desagrupar",
+ "action.unlock-all": "Desbloquear todo",
+ "action.zoom-in": "Achegar",
+ "action.zoom-out": "Afastar",
+ "action.zoom-to-100": "Zoom ao 100%",
+ "action.zoom-to-fit": "Axustar á ventá",
+ "action.zoom-to-selection": "Achegar á selección",
+ "actions-menu.title": "Accións",
+ "align-style.end": "Fin",
+ "align-style.justify": "Xustificar",
+ "align-style.middle": "Medio",
+ "align-style.start": "Comezo",
+ "arrowheadEnd-style.arrow": "Frecha",
+ "arrowheadEnd-style.bar": "Barra",
+ "arrowheadEnd-style.diamond": "Diamante",
+ "arrowheadEnd-style.dot": "Punto",
+ "arrowheadEnd-style.inverted": "Invertida",
+ "arrowheadEnd-style.none": "Ningunha",
+ "arrowheadEnd-style.pipe": "Tubo",
+ "arrowheadEnd-style.square": "Cadrado",
+ "arrowheadEnd-style.triangle": "Triángulo",
+ "arrowheadStart-style.arrow": "Frecha",
+ "arrowheadStart-style.bar": "Barra",
+ "arrowheadStart-style.diamond": "Diamante",
+ "arrowheadStart-style.dot": "Punto",
+ "arrowheadStart-style.inverted": "Invertida",
+ "arrowheadStart-style.none": "Ningún",
+ "arrowheadStart-style.pipe": "Tubo",
+ "arrowheadStart-style.square": "Cadrado",
+ "arrowheadStart-style.triangle": "Triángulo",
+ "color-style.black": "Negro",
+ "color-style.blue": "Azul",
+ "color-style.green": "Verde",
+ "color-style.grey": "Gris",
+ "color-style.light-blue": "Azul claro",
+ "color-style.light-green": "Verde claro",
+ "color-style.light-red": "Vermello claro",
+ "color-style.light-violet": "Violeta claro",
+ "color-style.orange": "Laranxa",
+ "color-style.red": "Vermello",
+ "color-style.violet": "Violeta",
+ "color-style.yellow": "Amarelo",
+ "context-menu.arrange": "Organizar",
+ "context-menu.copy-as": "Copiar como",
+ "context-menu.export-as": "Exportar como",
+ "context-menu.move-to-page": "Mover á páxina",
+ "context-menu.reorder": "Reordenar",
+ "context.pages.new-page": "Nova páxina",
+ "cursor-chat.type-to-chat": "Escribe para falar...",
+ "dash-style.dashed": "Discontínuo",
+ "dash-style.dotted": "Punteado",
+ "dash-style.draw": "Debuxar",
+ "dash-style.solid": "Contínuo",
+ "debug-panel.more": "Máis",
+ "edit-link-dialog.cancel": "Cancelar",
+ "edit-link-dialog.clear": "Limpar",
+ "edit-link-dialog.detail": "As ligazóns abriranse nunha nova lapela",
+ "edit-link-dialog.invalid-url": "Unha ligazón ten que ser unha URL válida.",
+ "edit-link-dialog.save": "Continuar",
+ "edit-link-dialog.title": "Editar ligazón",
+ "edit-link-dialog.url": "URL",
+ "edit-pages-dialog.move-down": "Mover abaixo",
+ "edit-pages-dialog.move-up": "Mover arriba",
+ "embed-dialog.back": "Atrás",
+ "embed-dialog.cancel": "Cancelar",
+ "embed-dialog.create": "Crear",
+ "embed-dialog.instruction": "Pega a URL do sitio para crear o embed.",
+ "embed-dialog.invalid-url": "Non puidemos crear o embed de esa URL.",
+ "embed-dialog.title": "Crear embed",
+ "embed-dialog.url": "URL",
+ "file-system.confirm-clear.cancel": "Cancelar",
+ "file-system.confirm-clear.continue": "Continuar",
+ "file-system.confirm-clear.description": "Abrir un arquivo vai substituír o actual proxecto e calquera cambio sen gardar perderase. Estás seguro de que queres continuar?",
+ "file-system.confirm-clear.dont-show-again": "Non preguntar outra vez",
+ "file-system.confirm-clear.title": "Sobrescribir o proxecto actual?",
+ "file-system.confirm-open.cancel": "Cancelar",
+ "file-system.confirm-open.description": "Abrir un arquivo vai substituír o actual proxecto e calquera cambio sen gardar perderase. Estás seguro de que queres continuar?",
+ "file-system.confirm-open.dont-show-again": "Non preguntar outra vez",
+ "file-system.confirm-open.open": "Abrir arquivo",
+ "file-system.confirm-open.title": "Sobrescribir o proxecto actual?",
+ "file-system.file-open-error.file-format-version-too-new": "O arquivo que intentaches abrir é dunha versión máis nova de tldraw. Por favor, refresca a páxina e probar outra vez.",
+ "file-system.file-open-error.generic-corrupted-file": "O arquivo que intentou abrir está corrompido.",
+ "file-system.file-open-error.not-a-tldraw-file": "O arquivo que intentaches abrir non parece un arquivo de tldraw.",
+ "file-system.file-open-error.title": "Non se puido abrir o arquivo",
+ "file-system.shared-document-file-open-error.description": "Abrir arquivos dende proxectos compartidos non está permitido.",
+ "file-system.shared-document-file-open-error.title": "Non se puido abrir o arquivo",
+ "fill-style.none": "Ningún",
+ "fill-style.pattern": "Patrón",
+ "fill-style.semi": "Media",
+ "fill-style.solid": "Contínuo",
+ "focus-mode.toggle-focus-mode": "Modo concentración",
+ "font-style.draw": "Debuxar",
+ "font-style.mono": "Mono",
+ "font-style.sans": "Sans",
+ "font-style.serif": "Serif",
+ "geo-style.arrow-down": "Frecha abaixo",
+ "geo-style.arrow-left": "Frecha esquerda",
+ "geo-style.arrow-right": "Frecha dereita",
+ "geo-style.arrow-up": "Frecha arriba",
+ "geo-style.check-box": "Checkbox",
+ "geo-style.cloud": "Nube",
+ "geo-style.diamond": "Diamante",
+ "geo-style.ellipse": "Elipse",
+ "geo-style.hexagon": "Hexágono",
+ "geo-style.octagon": "Octógono",
+ "geo-style.oval": "Óvalo",
+ "geo-style.pentagon": "Pentágono",
+ "geo-style.rectangle": "Rectángulo",
+ "geo-style.rhombus": "Rombo",
+ "geo-style.rhombus-2": "Rombo 2",
+ "geo-style.star": "Estrela",
+ "geo-style.trapezoid": "Trapecio",
+ "geo-style.triangle": "Triángulo",
+ "geo-style.x-box": "X Box",
+ "help-menu.about": "Sobre",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "Atallos de teclado",
+ "help-menu.title": "Axuda e recursos",
+ "help-menu.twitter": "Twitter",
+ "home-project-dialog.description": "Este é o teu proxecto local persoal. É só para ti!",
+ "home-project-dialog.ok": "Ok",
+ "home-project-dialog.title": "Proxecto persoal",
+ "menu.copy-as": "Copiar como",
+ "menu.edit": "Editar",
+ "menu.export-as": "Exportar como",
+ "menu.file": "Arquivo",
+ "menu.language": "Idioma",
+ "menu.preferences": "Preferencias",
+ "menu.title": "Menú",
+ "menu.view": "Ver",
+ "navigation-zone.toggle-minimap": "Alternar minimapa",
+ "navigation-zone.zoom": "Zoom",
+ "opacity-style.0.1": "10%",
+ "opacity-style.0.25": "25%",
+ "opacity-style.0.5": "50%",
+ "opacity-style.0.75": "75%",
+ "opacity-style.1": "100%",
+ "page-menu.create-new-page": "Crear páxina",
+ "page-menu.edit-done": "Feito",
+ "page-menu.edit-start": "Editar",
+ "page-menu.go-to-page": "Ir á páxina",
+ "page-menu.max-page-count-reached": "Alcanzouse o máximo de páxinas",
+ "page-menu.new-page-initial-name": "Páxina 1",
+ "page-menu.submenu.delete": "Borrar",
+ "page-menu.submenu.duplicate-page": "Duplicar",
+ "page-menu.submenu.move-down": "Mover abaixo",
+ "page-menu.submenu.move-up": "Mover arriba",
+ "page-menu.submenu.rename": "Renomear",
+ "page-menu.submenu.title": "Menú",
+ "page-menu.title": "Páxinas",
+ "people-menu.change-color": "Cambiar cor",
+ "people-menu.change-name": "Cambiar nome",
+ "people-menu.follow": "Seguir",
+ "people-menu.following": "Seguindo",
+ "people-menu.invite": "Invitar a outros",
+ "people-menu.leading": "Seguíndote",
+ "people-menu.title": "Xente",
+ "people-menu.user": "(Ti)",
+ "rename-project-dialog.cancel": "Cancelar",
+ "rename-project-dialog.rename": "Renomear",
+ "rename-project-dialog.title": "Renomear proxecto",
+ "share-menu.copy-link": "Copiar invitación",
+ "share-menu.copy-link-note": "Calquera ca ligazón poderá ver e editar este proxecto.",
+ "share-menu.copy-readonly-link": "Copiar invitación (só lectura)",
+ "share-menu.copy-readonly-link-note": "Calquera ca ligazón poderá ver (pero non editar) este proxecto.",
+ "share-menu.create-snapshot-link": "Crear ligazón de instantánea",
+ "share-menu.default-project-name": "Proxecto compartido",
+ "share-menu.fork-note": "Crea un novo proxecto compartido baseado nesta instantánea.",
+ "share-menu.offline-note": "Compartir este proxecto vai crear unha copia aloxada nunha nova URL. Podes compartir a URL con ata trinta personas para ver e editar o proxecto xuntos.",
+ "share-menu.project-too-large": "Sentímolo, este proxecto non pode ser compartido porque é moi grande. Estamos traballando nelo!",
+ "share-menu.readonly-link": "Só-Lectura",
+ "share-menu.save-note": "Descarga este proxecto no teu ordenador como ficheiro .tldr.",
+ "share-menu.share-project": "Compartir este proxecto",
+ "share-menu.snapshot-link-note": "Captura e comparte este proxecto como unha ligazón de instantánea de só lectura.",
+ "share-menu.title": "Compartir",
+ "share-menu.upload-failed": "Sentímolo, non puidemos subir o teu proxecto neste momento. Por favor téntao de novo ou infórmanos se o problema persiste.",
+ "sharing.confirm-leave.cancel": "Cancelar",
+ "sharing.confirm-leave.description": "Estás seguro de que queres saír deste proxecto compartido? Podes voltar a el navegando a esta URL.",
+ "sharing.confirm-leave.dont-show-again": "Non preguntar de novo",
+ "sharing.confirm-leave.leave": "Saír",
+ "sharing.confirm-leave.title": "Saír do proxecto actual?",
+ "shortcuts-dialog.collaboration": "Colaboración",
+ "shortcuts-dialog.edit": "Editar",
+ "shortcuts-dialog.file": "Arquivo",
+ "shortcuts-dialog.preferences": "Preferencias",
+ "shortcuts-dialog.title": "Atallos de teclado",
+ "shortcuts-dialog.tools": "Ferramentas",
+ "shortcuts-dialog.transform": "Transformar",
+ "shortcuts-dialog.view": "Ver",
+ "size-style.l": "Grande",
+ "size-style.m": "Mediano",
+ "size-style.s": "Pequeno",
+ "size-style.xl": "Extra grande",
+ "spline-style.cubic": "Cúbico",
+ "spline-style.line": "Liña",
+ "style-panel.align": "Aliñamento",
+ "style-panel.arrowhead-end": "Fin",
+ "style-panel.arrowhead-start": "Comezo",
+ "style-panel.arrowheads": "Puntas de frecha",
+ "style-panel.color": "Cor",
+ "style-panel.dash": "Liña",
+ "style-panel.fill": "Recheo",
+ "style-panel.font": "Fonte",
+ "style-panel.geo": "Forma",
+ "style-panel.mixed": "Mesturado",
+ "style-panel.opacity": "Opacidade",
+ "style-panel.position": "Posición",
+ "style-panel.size": "Tamaño",
+ "style-panel.spline": "Spline",
+ "style-panel.title": "Estilos",
+ "style-panel.vertical-align": "Aliñar verticalmente",
+ "toast.close": "Pechar",
+ "toast.error.copy-fail.desc": "Erro ao copiar a imaxe",
+ "toast.error.copy-fail.title": "Erro na copia",
+ "toast.error.export-fail.desc": "Erro ao exportar a imaxe",
+ "toast.error.export-fail.title": "Erro na exportación",
+ "tool-panel.drawing": "Debuxo",
+ "tool-panel.more": "Máis",
+ "tool-panel.shapes": "Formas",
+ "tool.arrow": "Frecha",
+ "tool.arrow-down": "Frecha abaixo",
+ "tool.arrow-left": "Frecha esquerda",
+ "tool.arrow-right": "Frecha dereita",
+ "tool.arrow-up": "Frecha arriba",
+ "tool.asset": "Activo",
+ "tool.check-box": "Checkbox",
+ "tool.cloud": "Nube",
+ "tool.diamond": "Diamante",
+ "tool.draw": "Debuxar",
+ "tool.ellipse": "Elipse",
+ "tool.embed": "Embed",
+ "tool.eraser": "Borrador",
+ "tool.frame": "Marco",
+ "tool.hand": "Man",
+ "tool.hexagon": "Hexágono",
+ "tool.highlight": "Destacar",
+ "tool.laser": "Láser",
+ "tool.line": "Liña",
+ "tool.note": "Pegatina",
+ "tool.octagon": "Octógono",
+ "tool.oval": "Óvalo",
+ "tool.pentagon": "Pentágono",
+ "tool.rectangle": "Rectángulo",
+ "tool.rhombus": "Rombo",
+ "tool.select": "Seleccionar",
+ "tool.star": "Estrela",
+ "tool.text": "Texto",
+ "tool.trapezoid": "Trapecio",
+ "tool.triangle": "Triángulo",
+ "tool.x-box": "X box",
+ "vscode.file-open.backup": "Copia de seguridade",
+ "vscode.file-open.backup-failed": "Fallou a copia de seguridade: este non é un arquivo .tldr.",
+ "vscode.file-open.backup-saved": "Copia de seguridade gardada",
+ "vscode.file-open.desc": "Este arquivo foi creado cunha versión antiga de tldraw. Queres actualizalo para que funcione ca nova versión?",
+ "vscode.file-open.dont-show-again": "Non preguntar outra vez",
+ "vscode.file-open.open": "Continuar"
+}
diff --git a/apps/web/public/translations/he.json b/apps/web/public/translations/he.json
new file mode 100644
index 00000000..8ecb9437
--- /dev/null
+++ b/apps/web/public/translations/he.json
@@ -0,0 +1,86 @@
+{
+ "action.bring-forward": "הזז קדימה",
+ "action.bring-to-front": "הבא לחזית",
+ "action.copy": "העתק",
+ "action.cut": "גזור",
+ "action.delete": "מחק",
+ "action.duplicate": "שכפל",
+ "action.flip-horizontal": "הפוך אופקית",
+ "action.flip-vertical": "הפוך אנכית",
+ "action.flip-horizontal.short": "הפוך אופקית",
+ "action.flip-vertical.short": "הפוך אנכית",
+ "action.group": "קבץ",
+ "action.insert-media": "העלאת מדיה",
+ "action.paste": "הדבק",
+ "action.redo": "עשה מחדש",
+ "action.select-all": "בחר הכל",
+ "action.select-none": "בטל בחירה",
+ "action.send-backward": "הזז אחורה",
+ "action.send-to-back": "הבא לתחתית",
+ "action.toggle-dark-mode.menu": "מצב חשוך",
+ "action.toggle-dark-mode": "מצב חשוך",
+ "action.toggle-debug-mode.menu": "מצב דיבאג",
+ "action.toggle-debug-mode": "מצב דיבאג",
+ "action.toggle-focus-mode.menu": "מצב פוקוס",
+ "action.toggle-focus-mode": "מצב פוקוס",
+ "action.toggle-grid.menu": "(גריד)הראה רשת עימוד",
+ "action.toggle-grid": "(גריד)הראה רשת עימוד",
+ "action.toggle-snap-mode.menu": "הראה קווי מתאר",
+ "action.toggle-snap-mode": "הראה קווי מתאר",
+ "action.undo": "בטל",
+ "action.ungroup": "בטל קיבוץ",
+ "action.zoom-in": "הגדל תצוגה",
+ "action.zoom-out": "הקטן תצוגה",
+ "action.zoom-to-fit": "זום להתאמה",
+ "action.zoom-to-selection": "זום לבחירה",
+ "dash-style.draw": "צייר",
+ "font-style.draw": "צייר",
+ "geo-style.ellipse": "אליפסה",
+ "geo-style.rectangle": "מרובע",
+ "geo-style.triangle": "משולש",
+ "arrowheadStart-style.arrow": "חץ",
+ "arrowheadStart-style.triangle": "משולש",
+ "arrowheadEnd-style.arrow": "חץ",
+ "arrowheadEnd-style.triangle": "משולש",
+ "spline-style.line": "קו",
+ "tool.select": "סמן",
+ "tool.draw": "צייר",
+ "tool.eraser": "מחק",
+ "tool.arrow": "חץ",
+ "tool.ellipse": "אליפסה",
+ "tool.line": "קו",
+ "tool.rectangle": "מרובע",
+ "tool.triangle": "משולש",
+ "tool.note": "דביקי",
+ "tool.text": "טקסט",
+ "menu.copy-as": "העתק כ",
+ "menu.edit": "עריכה",
+ "menu.export-as": "ייצא כ",
+ "menu.file": "קובץ",
+ "menu.language": "שפה",
+ "menu.preferences": "מאפיינים",
+ "menu.view": "תצוגה",
+ "context-menu.copy-as": "העתק כ",
+ "context-menu.export-as": "ייצא כ",
+ "context-menu.move-to-page": "הזז לדף",
+ "page-menu.create-new-page": "צור דף",
+ "page-menu.edit-start": "עריכה",
+ "page-menu.submenu.duplicate-page": "שכפל",
+ "page-menu.submenu.delete": "מחק",
+ "share-menu.copy-link": "העתק קישור הזמנה",
+ "edit-link-dialog.cancel": "בטל",
+ "embed-dialog.cancel": "בטל",
+ "shortcuts-dialog.edit": "עריכה",
+ "shortcuts-dialog.file": "קובץ",
+ "shortcuts-dialog.preferences": "מאפיינים",
+ "shortcuts-dialog.view": "תצוגה",
+ "style-panel.title": "עיצוב",
+ "style-panel.align": "יישור",
+ "style-panel.color": "צבע",
+ "style-panel.dash": "גבול",
+ "style-panel.fill": "מלא",
+ "style-panel.font": "גופן",
+ "style-panel.size": "גודל",
+ "focus-mode.toggle-focus-mode": "מצב פוקוס",
+ "file-system.confirm-open.cancel": "בטל"
+}
diff --git a/apps/web/public/translations/hi-in.json b/apps/web/public/translations/hi-in.json
new file mode 100644
index 00000000..9d05dd4c
--- /dev/null
+++ b/apps/web/public/translations/hi-in.json
@@ -0,0 +1,290 @@
+{
+ "action.align-bottom": "नीचे की तरफ अलाइन करें",
+ "action.align-center-horizontal": "आड़ा अलाइन करें",
+ "action.align-center-horizontal.short": "H को अलाइन करें",
+ "action.align-center-vertical": "लंबवत अलाइन करें",
+ "action.align-center-vertical.short": "V को अलाइन करें",
+ "action.align-left": "बाईं तरफ अलाइन करें",
+ "action.align-right": "दाहिनी तरफ अलाइन करें",
+ "action.align-top": "ऊपर से अलाइन करें",
+ "action.back-to-content": "कंटेन्ट पर वापस जाएं",
+ "action.bring-forward": "आगे लाएं",
+ "action.bring-to-front": "सामने लाएं",
+ "action.convert-to-bookmark": "बुकमार्क में कनवर्ट करें",
+ "action.convert-to-embed": "एम्बेड में कनवर्ट करें",
+ "action.copy": "कॉपी करें",
+ "action.copy-as-json": "JSON के रूप में कॉपी करें",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "PNG के रूप में कॉपी करें",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "SVG के रूप में कॉपी करें",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "कट करें",
+ "action.delete": "डिलीट करें",
+ "action.distribute-horizontal": "आड़ा वितरित करें",
+ "action.distribute-horizontal.short": "H को वितरित करें",
+ "action.distribute-vertical": "लंबवत वितरित करें",
+ "action.distribute-vertical.short": "V को वितरित करें",
+ "action.duplicate": "डुप्लिकेट",
+ "action.edit-link": "लिंक को एडिट करें",
+ "action.exit-pen-mode": "पेन मोड से बाहर निकलें",
+ "action.export-as-json": "JSON के रूप में एक्सपोर्ट करें",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "PNG के रूप में एक्सपोर्ट करें",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "SVG के रूप में एक्सपोर्ट करें",
+ "action.export-as-svg.short": "SVG",
+ "action.flip-horizontal": "आड़ा फ्लिप करें",
+ "action.flip-horizontal.short": "H को फ्लिप करें",
+ "action.flip-vertical": "लंबवत फ्लिप करें",
+ "action.flip-vertical.short": "V को फ्लिप करें",
+ "action.group": "ग्रुप",
+ "action.insert-media": "मीडिया अपलोड करें",
+ "action.new-shared-project": "नया शेयर किया हुआ प्रोजेक्ट",
+ "action.open-embed-link": "लिंक खोलें",
+ "action.open-file": "फ़ाइल खोलें",
+ "action.pack": "पैक करें",
+ "action.paste": "पेस्ट करें",
+ "action.print": "प्रिंट करें",
+ "action.redo": "रिडू करें",
+ "action.rotate-ccw": "घड़ी की सुई के विपरीत दिशा में घुमाएं",
+ "action.rotate-cw": "घड़ी की सुई की दिशा में घुमाएं",
+ "action.save-copy": "कॉपी सेव करें",
+ "action.select-all": "सभी चुनें",
+ "action.select-none": "कुछ मत चुनें",
+ "action.send-backward": "पीछे भेजें",
+ "action.send-to-back": "वापस की तरफ भेजें",
+ "action.share-project": "इस प्रोजेक्ट को शेयर करें",
+ "action.stack-horizontal": "आड़ा स्टैक करें",
+ "action.stack-horizontal.short": "H को स्टैक करें",
+ "action.stack-vertical": "लंबवत स्टैक करें",
+ "action.stack-vertical.short": "V को स्टैक करें",
+ "action.stretch-horizontal": "आड़ा खींचें",
+ "action.stretch-horizontal.short": "H खींचें",
+ "action.stretch-vertical": "लंबवत खींचे",
+ "action.stretch-vertical.short": "V खींचें",
+ "action.toggle-auto-size": "ऑटो साइज़ टॉगल करें",
+ "action.toggle-dark-mode": "डार्क मोड टॉगल करें",
+ "action.toggle-dark-mode.menu": "डार्क मोड",
+ "action.toggle-debug-mode": "डीबग मोड टॉगल करें",
+ "action.toggle-debug-mode.menu": "डीबग मोड",
+ "action.toggle-focus-mode": "फोकस मोड टॉगल करें",
+ "action.toggle-focus-mode.menu": "फोकस मोड",
+ "action.toggle-grid": "ग्रिड टॉगल करें",
+ "action.toggle-grid.menu": "ग्रिड दिखाएं",
+ "action.toggle-snap-mode": "टॉगल हमेशा स्नैप करें",
+ "action.toggle-snap-mode.menu": "हमेशा स्नैप करें",
+ "action.toggle-tool-lock": "टूल लॉक टॉगल करें",
+ "action.toggle-tool-lock.menu": "टूल लॉक",
+ "action.toggle-transparent": "पारदर्शी बैक्ग्राउण्ड को टॉगल करें",
+ "action.toggle-transparent.context-menu": "पारदर्शी",
+ "action.toggle-transparent.menu": "पारदर्शी",
+ "action.undo": "अनडू करें",
+ "action.ungroup": "अनग्रुप करें",
+ "action.zoom-in": "ज़ूम इन करें",
+ "action.zoom-out": "ज़ूम आउट करें",
+ "action.zoom-to-100": "100% तक ज़ूम करें",
+ "action.zoom-to-fit": "फिट करने तक लिए ज़ूम करें",
+ "action.zoom-to-selection": "चयन तक ज़ूम करें",
+ "actions-menu.title": "कार्य",
+ "align-style.end": "अंत",
+ "align-style.justify": "जस्टीफ़ाई करें",
+ "align-style.middle": "मध्य",
+ "align-style.start": "शुरुआत",
+ "arrowheadEnd-style.arrow": "एरो",
+ "arrowheadEnd-style.bar": "बार",
+ "arrowheadEnd-style.diamond": "डायमंड",
+ "arrowheadEnd-style.dot": "डॉट",
+ "arrowheadEnd-style.inverted": "उल्टा",
+ "arrowheadEnd-style.none": "कोई नहीं",
+ "arrowheadEnd-style.pipe": "पाइप",
+ "arrowheadEnd-style.square": "वर्ग",
+ "arrowheadEnd-style.triangle": "त्रिकोण",
+ "arrowheadStart-style.arrow": "एरो",
+ "arrowheadStart-style.bar": "बार",
+ "arrowheadStart-style.diamond": "डायमंड",
+ "arrowheadStart-style.dot": "डॉट",
+ "arrowheadStart-style.inverted": "उल्टा",
+ "arrowheadStart-style.none": "कोई नहीं",
+ "arrowheadStart-style.pipe": "पाइप",
+ "arrowheadStart-style.square": "वर्ग",
+ "arrowheadStart-style.triangle": "त्रिकोण",
+ "color-style.black": "काला",
+ "color-style.blue": "नीला",
+ "color-style.green": "हरा",
+ "color-style.grey": "ग्रे",
+ "color-style.light-blue": "हल्का नीला",
+ "color-style.light-green": "हल्का हरा",
+ "color-style.light-red": "हलका लाल",
+ "color-style.light-violet": "हल्का बैंगनी",
+ "color-style.orange": "ऑरेंज",
+ "color-style.red": "लाल",
+ "color-style.violet": "बैंगनी",
+ "color-style.yellow": "पीला",
+ "context-menu.arrange": "व्यवस्थित करें",
+ "context-menu.copy-as": "के रूप में कॉपी करें",
+ "context-menu.export-as": "के रूप में एक्सपोर्ट करें",
+ "context-menu.move-to-page": "पेज पर जाएं",
+ "context-menu.reorder": "रीआर्डर करें",
+ "dash-style.dashed": "डैश्ड",
+ "dash-style.dotted": "डोटेड",
+ "dash-style.draw": "ड्रॉ",
+ "dash-style.solid": "ठोस",
+ "edit-link-dialog.cancel": "रद्द करें",
+ "edit-link-dialog.clear": "साफ़ करें",
+ "edit-link-dialog.detail": "लिंक एक नए टैब में खुलेंगे।",
+ "edit-link-dialog.invalid-url": "लिंक एक मान्य URL होना चाहिए।",
+ "edit-link-dialog.save": "जारी रखें",
+ "edit-link-dialog.title": "लिंक को एडिट करें",
+ "edit-link-dialog.url": "URL",
+ "edit-pages-dialog.move-down": "नीचे जाएं",
+ "edit-pages-dialog.move-up": "ऊपर जाएं",
+ "embed-dialog.back": "वापस जाएं",
+ "embed-dialog.cancel": "रद्द करें",
+ "embed-dialog.create": "बनाएं",
+ "embed-dialog.instruction": "एम्बेड बनाने के लिए साइट के URL में पेस्ट करें।",
+ "embed-dialog.invalid-url": "हम उस URL से एम्बेड नहीं बना सके।",
+ "embed-dialog.title": "एम्बेड बनाएं",
+ "embed-dialog.url": "URL",
+ "file-system.confirm-open.cancel": "रद्द करें",
+ "file-system.confirm-open.description": "फ़ाइल खोलने से आपका वर्तमान प्रोजेक्ट बदल जाएगा और सेव नहीं किए गए परिवर्तन खो जाएंगे। क्या आप वाकई जारी रखना चाहते हैं?",
+ "file-system.confirm-open.dont-show-again": "दोबारा न पूछें",
+ "file-system.confirm-open.open": "फ़ाइल खोलें",
+ "file-system.confirm-open.title": "वर्तमान प्रोजेक्ट को ओवरराइट करना चाहते हैं?",
+ "file-system.file-open-error.file-format-version-too-new": "आपने जिस फ़ाइल को खोलने का प्रयास किया है वह tldraw के नए वर्ज़न से है। कृपया पेज लोड करें और दोबारा कोशिश करें।",
+ "file-system.file-open-error.generic-corrupted-file": "आपने जिस फ़ाइल को खोलने का प्रयास किया वह करप्ट है।",
+ "file-system.file-open-error.not-a-tldraw-file": "आपने जिस फ़ाइल को खोलने का प्रयास किया है वह tldraw फ़ाइल की तरह नहीं दिखती है।",
+ "file-system.file-open-error.title": "फ़ाइल को खोल नहीं सके",
+ "fill-style.none": "कोई नहीं",
+ "fill-style.pattern": "पैटर्न",
+ "fill-style.semi": "सेमी",
+ "fill-style.solid": "ठोस",
+ "focus-mode.toggle-focus-mode": "फोकस मोड टॉगल करें",
+ "font-style.draw": "ड्रॉ",
+ "font-style.mono": "मोनो",
+ "font-style.sans": "सेंस",
+ "font-style.serif": "सेरिफ़",
+ "geo-style.arrow-down": "नीचे की तरफ एरो करें",
+ "geo-style.arrow-left": "बाईं तरफ एरो करें",
+ "geo-style.arrow-right": "दाहिनी तरफ एरो करें",
+ "geo-style.arrow-up": "ऊपर की तरफ एरो करें",
+ "geo-style.diamond": "डायमंड",
+ "geo-style.ellipse": "दीर्घवृत्त",
+ "geo-style.hexagon": "षट्कोण",
+ "geo-style.octagon": "अष्टकोण",
+ "geo-style.oval": "अंडाकार",
+ "geo-style.pentagon": "पंचकोण",
+ "geo-style.rectangle": "समकोण",
+ "geo-style.rhombus": "विषमकोण",
+ "geo-style.rhombus-2": "विषमकोण 2",
+ "geo-style.star": "स्टार",
+ "geo-style.trapezoid": "चतुर्भुज",
+ "geo-style.triangle": "त्रिकोण",
+ "geo-style.x-box": "X बॉक्स",
+ "help-menu.about": "के बारे में",
+ "help-menu.discord": "विवाद",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "किबोर्ड शॉर्टकट",
+ "help-menu.title": "सहायता और संसाधन",
+ "help-menu.twitter": "Twitter",
+ "menu.copy-as": "के रूप में कॉपी करें",
+ "menu.edit": "एडिट करें",
+ "menu.export-as": "के रूप में एक्सपोर्ट करें",
+ "menu.file": "फ़ाइल",
+ "menu.language": "भाषा",
+ "menu.preferences": "पसंद",
+ "menu.title": "मेन्यू",
+ "menu.view": "देखें",
+ "navigation-zone.toggle-minimap": "मिनिमैप टॉगल करें",
+ "navigation-zone.zoom": "ज़ूम करें",
+ "opacity-style.0.1": "10%",
+ "opacity-style.0.25": "25%",
+ "opacity-style.0.5": "50%",
+ "opacity-style.0.75": "75%",
+ "opacity-style.1": "100%",
+ "page-menu.create-new-page": "नया पेज बनाएं",
+ "page-menu.edit-done": "हो गया",
+ "page-menu.edit-start": "एडिट करें",
+ "page-menu.max-page-count-reached": "अधिकतम पेज की सीमा पूर्ण हुई",
+ "page-menu.new-page-initial-name": "पेज 1",
+ "page-menu.submenu.delete": "डिलीट करें",
+ "page-menu.submenu.duplicate-page": "डुप्लिकेट",
+ "page-menu.submenu.move-down": "नीचे जाएं",
+ "page-menu.submenu.move-up": "ऊपर जाएं",
+ "page-menu.submenu.rename": "नाम बदलें",
+ "page-menu.submenu.title": "मेन्यू",
+ "page-menu.title": "पेजिस",
+ "people-menu.change-color": "कलर बदलें",
+ "people-menu.change-name": "नाम बदलें",
+ "people-menu.invite": "दूसरों को इन्वाइट करें",
+ "people-menu.title": "लोग",
+ "people-menu.user": "(आप)",
+ "share-menu.copy-link": "लिंक कॉपी करें",
+ "share-menu.copy-link-note": "लिंक वाला कोई भी व्यक्ति इस प्रोजेक्ट को देख और एडिट कर सकेगा।",
+ "share-menu.copy-readonly-link": "रीड-ओनली लिंक कॉपी करें",
+ "share-menu.copy-readonly-link-note": "लिंक वाला कोई भी व्यक्ति इस प्रोजेक्ट को देख (लेकिन एडिट नहीं) पाएगा।",
+ "share-menu.offline-note": "इस प्रोजेक्ट को शेयर करने से नए URL पर होस्ट की गई लाइव कॉपी बन जाएगी। प्रोजेक्ट को एक साथ देखने और एडिट करने के लिए आप अधिकतम तीस अन्य लोगों के साथ URL शेयर कर सकते हैं।",
+ "share-menu.project-too-large": "क्षमा करें, यह प्रोजेक्ट शेयर नहीं किया जा सकता क्योंकि यह बहुत बड़ा है। हम इस पर काम कर रहे हैं!",
+ "share-menu.readonly-link": "रीड-ओनली",
+ "share-menu.share-project": "इस प्रोजेक्ट को शेयर करें",
+ "share-menu.title": "शेयर करें",
+ "shortcuts-dialog.edit": "एडिट करें",
+ "shortcuts-dialog.file": "फ़ाइल",
+ "shortcuts-dialog.preferences": "पसंद",
+ "shortcuts-dialog.title": "किबोर्ड शॉर्टकट",
+ "shortcuts-dialog.tools": "टूल्स",
+ "shortcuts-dialog.transform": "परिवर्तन करें",
+ "shortcuts-dialog.view": "देखें",
+ "size-style.l": "बड़ा",
+ "size-style.m": "मध्यम",
+ "size-style.s": "छोटा",
+ "size-style.xl": "बहुत बड़ा",
+ "spline-style.cubic": "घन",
+ "spline-style.line": "पंक्ति",
+ "style-panel.align": "अलाइन करें",
+ "style-panel.arrowheads": "तीर",
+ "style-panel.color": "कलर",
+ "style-panel.dash": "डैश",
+ "style-panel.fill": "भरें",
+ "style-panel.font": "फॉन्ट",
+ "style-panel.geo": "शेप",
+ "style-panel.mixed": "मिश्रित",
+ "style-panel.opacity": "ओपैसटी",
+ "style-panel.size": "साइज़",
+ "style-panel.spline": "स्प्लाइन",
+ "style-panel.title": "शैलियां",
+ "toast.close": "बंद करें",
+ "toast.error.copy-fail.desc": "इमेज कॉपी करने में विफल",
+ "toast.error.copy-fail.title": "विफल कॉपी",
+ "toast.error.export-fail.desc": "इमेज एक्सपोर्ट करने में विफल",
+ "toast.error.export-fail.title": "विफल एक्सपोर्ट",
+ "tool-panel.drawing": "ड्रौइंग",
+ "tool-panel.shapes": "शेप्स",
+ "tool.arrow": "एरो",
+ "tool.arrow-down": "नीचे की तरफ एरो करें",
+ "tool.arrow-left": "बाईं तरफ एरो करें",
+ "tool.arrow-right": "दाहिनी तरफ एरो करें",
+ "tool.arrow-up": "ऊपर की तरफ एरो करें",
+ "tool.asset": "संपत्ति",
+ "tool.diamond": "डायमंड",
+ "tool.draw": "ड्रॉ",
+ "tool.ellipse": "दीर्घवृत्त",
+ "tool.embed": "एम्बेड",
+ "tool.eraser": "इरेज़र",
+ "tool.frame": "फ्रेम",
+ "tool.hand": "हाथ",
+ "tool.hexagon": "षट्कोण",
+ "tool.line": "रेखा",
+ "tool.note": "नोट",
+ "tool.octagon": "अष्टकोण",
+ "tool.oval": "अंडाकार",
+ "tool.pentagon": "पंचकोण",
+ "tool.rectangle": "समकोण",
+ "tool.rhombus": "विषमकोण",
+ "tool.select": "चुनें",
+ "tool.star": "स्टार",
+ "tool.text": "टेक्स्ट",
+ "tool.trapezoid": "चतुर्भुज",
+ "tool.triangle": "त्रिकोण",
+ "tool.x-box": "X बॉक्स"
+}
diff --git a/apps/web/public/translations/hr.json b/apps/web/public/translations/hr.json
new file mode 100644
index 00000000..0640eb7c
--- /dev/null
+++ b/apps/web/public/translations/hr.json
@@ -0,0 +1,358 @@
+{
+ "action.align-bottom": "Poravnaj dno",
+ "action.align-center-horizontal": "Poravnaj vodoravno",
+ "action.align-center-horizontal.short": "Poravnaj H",
+ "action.align-center-vertical": "Poravnaj okomito",
+ "action.align-center-vertical.short": "Poravnaj V",
+ "action.align-left": "Poravnaj lijevo",
+ "action.align-right": "Poravnaj desno",
+ "action.align-top": "Poravnaj gore",
+ "action.back-to-content": "Natrag na sadržaj",
+ "action.bring-forward": "Prikaži",
+ "action.bring-to-front": "Stavi ispred",
+ "action.convert-to-bookmark": "Pretvori u oznaku",
+ "action.convert-to-embed": "Pretvori u ugradnju",
+ "action.copy": "Kopiraj",
+ "action.copy-as-json": "Kopiraj kao JSON",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "Kopiraj kao PNG",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "Kopiraj kao SVG",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "Izreži",
+ "action.delete": "Izbriši",
+ "action.distribute-horizontal": "Distribuiraj vodoravno",
+ "action.distribute-horizontal.short": "Distribuiraj H",
+ "action.distribute-vertical": "Distribuiraj okomito",
+ "action.distribute-vertical.short": "Distribuiraj V",
+ "action.duplicate": "Duplikat",
+ "action.edit-link": "Uredi vezu",
+ "action.exit-pen-mode": "Izađi iz načina rada olovke",
+ "action.export-as-json": "Izvezi kao JSON",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "Izvezi kao PNG",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "Izvezi kao SVG",
+ "action.export-as-svg.short": "SVG",
+ "action.fit-frame-to-content": "Prilagodi sadržaju",
+ "action.flip-horizontal": "Okreni vodoravno",
+ "action.flip-horizontal.short": "Okreni H",
+ "action.flip-vertical": "Okreni okomito",
+ "action.flip-vertical.short": "Okreni V",
+ "action.fork-project": "Račvajte ovaj projekt",
+ "action.group": "Grupa",
+ "action.insert-embed": "Umetni ugradnju",
+ "action.insert-media": "Učitaj medije",
+ "action.leave-shared-project": "Napusti zajednički projekt",
+ "action.new-project": "Novi projekt",
+ "action.new-shared-project": "Novi zajednički projekt",
+ "action.open-cursor-chat": "Časkanje kursora",
+ "action.open-embed-link": "Otvori vezu",
+ "action.open-file": "Otvori datoteku",
+ "action.pack": "Pakiraj",
+ "action.paste": "Zalijepi",
+ "action.print": "Ispis",
+ "action.redo": "Ponovi",
+ "action.remove-frame": "Ukloni okvir",
+ "action.rotate-ccw": "Zakreni u smjeru suprotnom od kazaljke na satu",
+ "action.rotate-cw": "Zakreni u smjeru kazaljke na satu",
+ "action.save-copy": "Spremi kopiju",
+ "action.select-all": "Odaberi sve",
+ "action.select-none": "Ne odaberite ništa",
+ "action.send-backward": "Pošalji unatrag",
+ "action.send-to-back": "Pošalji nazad",
+ "action.share-project": "Podijelite ovaj projekt",
+ "action.stack-horizontal": "Složi vodoravno",
+ "action.stack-horizontal.short": "Slagaj H",
+ "action.stack-vertical": "Složi okomito",
+ "action.stack-vertical.short": "Slagaj V",
+ "action.stop-following": "Prestani pratiti",
+ "action.stretch-horizontal": "Istegni vodoravno",
+ "action.stretch-horizontal.short": "Istegni H",
+ "action.stretch-vertical": "Istegni okomito",
+ "action.stretch-vertical.short": "Istegni V",
+ "action.toggle-auto-size": "Prebaci automatsku veličinu",
+ "action.toggle-dark-mode": "Prebaci tamni tema",
+ "action.toggle-dark-mode.menu": "Tamna tema",
+ "action.toggle-debug-mode": "Prebaci način otklanjanja pogrešaka",
+ "action.toggle-debug-mode.menu": "Način otklanjanja pogrešaka",
+ "action.toggle-edge-scrolling": "Prebaci rubno pomicanje",
+ "action.toggle-edge-scrolling.menu": "Pomicanje rubova",
+ "action.toggle-focus-mode": "Prebaci način rada fokusa",
+ "action.toggle-focus-mode.menu": "Način rada fokusa",
+ "action.toggle-grid": "Prebaci rešetku",
+ "action.toggle-grid.menu": "Prikaži rešetku",
+ "action.toggle-lock": "Prebaci zaključano",
+ "action.toggle-reduce-motion": "Prebaci smanjenje kretanja",
+ "action.toggle-reduce-motion.menu": "Smanji kretanje",
+ "action.toggle-snap-mode": "Prebaci prikvačivanje",
+ "action.toggle-snap-mode.menu": "Uvijek prikvačivanje",
+ "action.toggle-tool-lock": "Prebaci zaključavanje alata",
+ "action.toggle-tool-lock.menu": "Zaključavanje alata",
+ "action.toggle-transparent": "Prebaci prozirnu pozadinu",
+ "action.toggle-transparent.context-menu": "Prozirno",
+ "action.toggle-transparent.menu": "Prozirno",
+ "action.undo": "Poništi",
+ "action.ungroup": "Razgrupiraj",
+ "action.unlock-all": "Otključaj sve",
+ "action.zoom-in": "Uvećaj",
+ "action.zoom-out": "Smanji",
+ "action.zoom-to-100": "Povećaj na 100%",
+ "action.zoom-to-fit": "Zumiraj da stane sve",
+ "action.zoom-to-selection": "Povećaj na odabir",
+ "actions-menu.title": "Akcije",
+ "align-style.end": "Kraj",
+ "align-style.justify": "Obostran",
+ "align-style.middle": "Sredina",
+ "align-style.start": "Početak",
+ "arrowheadEnd-style.arrow": "Strelica",
+ "arrowheadEnd-style.bar": "Traka",
+ "arrowheadEnd-style.diamond": "Dijamant",
+ "arrowheadEnd-style.dot": "Točka",
+ "arrowheadEnd-style.inverted": "Obrnuto",
+ "arrowheadEnd-style.none": "Ništa",
+ "arrowheadEnd-style.pipe": "Cjev",
+ "arrowheadEnd-style.square": "Kvadrat",
+ "arrowheadEnd-style.triangle": "Trokut",
+ "arrowheadStart-style.arrow": "Strelica",
+ "arrowheadStart-style.bar": "Traka",
+ "arrowheadStart-style.diamond": "Dijamant",
+ "arrowheadStart-style.dot": "Točka",
+ "arrowheadStart-style.inverted": "Obrnuto",
+ "arrowheadStart-style.none": "Ništa",
+ "arrowheadStart-style.pipe": "Cjev",
+ "arrowheadStart-style.square": "Kvadrat",
+ "arrowheadStart-style.triangle": "Trokut",
+ "color-style.black": "Crna",
+ "color-style.blue": "Plava",
+ "color-style.green": "Zelena",
+ "color-style.grey": "Siva",
+ "color-style.light-blue": "Svijetloplava",
+ "color-style.light-green": "Svijetlo zelena",
+ "color-style.light-red": "Svijetlo crvena",
+ "color-style.light-violet": "Svijetloljubičasta",
+ "color-style.orange": "Narančasta",
+ "color-style.red": "Crvena",
+ "color-style.violet": "Ljubičasta",
+ "color-style.yellow": "Žuta",
+ "context-menu.arrange": "Rasporedi",
+ "context-menu.copy-as": "Kopiraj kao",
+ "context-menu.export-as": "Izvezi kao",
+ "context-menu.move-to-page": "Premjesti na stranicu",
+ "context-menu.reorder": "Promijeni redoslijed",
+ "context.pages.new-page": "Nova stranica",
+ "cursor-chat.type-to-chat": "Upišite za razgovor...",
+ "dash-style.dashed": "Iscrtkana",
+ "dash-style.dotted": "Točkasta",
+ "dash-style.draw": "Skica",
+ "dash-style.solid": "Puna",
+ "debug-panel.more": "Više",
+ "edit-link-dialog.cancel": "Odustani",
+ "edit-link-dialog.clear": "Očisti",
+ "edit-link-dialog.detail": "Veze će se otvoriti u novoj kartici.",
+ "edit-link-dialog.invalid-url": "Veza mora biti važeći URL.",
+ "edit-link-dialog.save": "Nastavi",
+ "edit-link-dialog.title": "Uredi vezu",
+ "edit-link-dialog.url": "URL",
+ "edit-pages-dialog.move-down": "Pomakni dolje",
+ "edit-pages-dialog.move-up": "Pomakni gore",
+ "embed-dialog.back": "Natrag",
+ "embed-dialog.cancel": "Odustani",
+ "embed-dialog.create": "Stvori",
+ "embed-dialog.instruction": "Zalijepite URL web-mjesta da biste stvorili ugradnju.",
+ "embed-dialog.invalid-url": "Nismo mogli stvoriti ugradnju iz tog URL-a.",
+ "embed-dialog.title": "Umetni ugrađeni",
+ "embed-dialog.url": "URL",
+ "file-system.confirm-clear.cancel": "Odustani",
+ "file-system.confirm-clear.continue": "Nastavi",
+ "file-system.confirm-clear.description": "Stvaranje novog projekta izbrisat će vaš trenutni projekt i sve nespremljene promjene bit će izgubljene. Jeste li sigurni da želite nastaviti?",
+ "file-system.confirm-clear.dont-show-again": "Ne pitaj više",
+ "file-system.confirm-clear.title": "Očistiti trenutni projekt?",
+ "file-system.confirm-open.cancel": "Odustani",
+ "file-system.confirm-open.description": "Otvaranje datoteke zamijenit će vaš trenutni projekt i sve nespremljene promjene bit će izgubljene. Jeste li sigurni da želite nastaviti?",
+ "file-system.confirm-open.dont-show-again": "Ne pitaj više",
+ "file-system.confirm-open.open": "Otvori datoteku",
+ "file-system.confirm-open.title": "Prebrisati trenutni projekt?",
+ "file-system.file-open-error.file-format-version-too-new": "Datoteka koju ste pokušali otvoriti je iz novije verzije tldraw. Ponovno učitajte stranicu i pokušajte ponovno.",
+ "file-system.file-open-error.generic-corrupted-file": "Datoteka koju ste pokušali otvoriti je oštećena.",
+ "file-system.file-open-error.not-a-tldraw-file": "Datoteka koju ste pokušali otvoriti ne izgleda kao tldraw datoteka.",
+ "file-system.file-open-error.title": "Nije moguće otvoriti datoteku",
+ "file-system.shared-document-file-open-error.description": "Otvaranje datoteka iz zajedničkih projekata nije podržano.",
+ "file-system.shared-document-file-open-error.title": "Nije moguće otvoriti datoteku",
+ "fill-style.none": "Ništa",
+ "fill-style.pattern": "Uzorka",
+ "fill-style.semi": "Pola",
+ "fill-style.solid": "Puna",
+ "focus-mode.toggle-focus-mode": "Prebaci način rada fokusa",
+ "font-style.draw": "Skica",
+ "font-style.mono": "Mono",
+ "font-style.sans": "Sans",
+ "font-style.serif": "Serif",
+ "geo-style.arrow-down": "Strelica prema dolje",
+ "geo-style.arrow-left": "Strelica lijevo",
+ "geo-style.arrow-right": "Strelica desno",
+ "geo-style.arrow-up": "Strelica prema gore",
+ "geo-style.check-box": "Potvrdni okvir",
+ "geo-style.cloud": "Oblak",
+ "geo-style.diamond": "Dijamant",
+ "geo-style.ellipse": "Elipsa",
+ "geo-style.hexagon": "Šesterokut",
+ "geo-style.octagon": "Osmerokut",
+ "geo-style.oval": "Oval",
+ "geo-style.pentagon": "Petokut",
+ "geo-style.rectangle": "Pravokutnik",
+ "geo-style.rhombus": "Romb",
+ "geo-style.rhombus-2": "Romb 2",
+ "geo-style.star": "Zvijezda",
+ "geo-style.trapezoid": "Trapez",
+ "geo-style.triangle": "Trokut",
+ "geo-style.x-box": "X kutija",
+ "help-menu.about": "O nama",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "Tipkovne prečice",
+ "help-menu.title": "Pomoć i resursi",
+ "help-menu.twitter": "Twitter",
+ "home-project-dialog.description": "Ovo je projekt vaše lokalne kuće. Samo je za vas!",
+ "home-project-dialog.ok": "U redu",
+ "home-project-dialog.title": "Domaći projekt",
+ "menu.copy-as": "Kopiraj kao",
+ "menu.edit": "Uredi",
+ "menu.export-as": "Izvezi kao",
+ "menu.file": "Datoteka",
+ "menu.language": "Jezik",
+ "menu.preferences": "Postavke",
+ "menu.title": "Izbornik",
+ "menu.view": "Prikaz",
+ "navigation-zone.toggle-minimap": "Prikaži minikartu",
+ "navigation-zone.zoom": "Zumiranje",
+ "opacity-style.0.1": "10%",
+ "opacity-style.0.25": "25%",
+ "opacity-style.0.5": "50%",
+ "opacity-style.0.75": "75%",
+ "opacity-style.1": "100%",
+ "page-menu.create-new-page": "Stvori novu stranicu",
+ "page-menu.edit-done": "Gotovo",
+ "page-menu.edit-start": "Uredi",
+ "page-menu.go-to-page": "Idi na stranicu",
+ "page-menu.max-page-count-reached": "Maksimum dosegnutih stranica",
+ "page-menu.new-page-initial-name": "Stranica 1",
+ "page-menu.submenu.delete": "Izbriši",
+ "page-menu.submenu.duplicate-page": "Dupliciraj",
+ "page-menu.submenu.move-down": "Pomakni dolje",
+ "page-menu.submenu.move-up": "Pomakni gore",
+ "page-menu.submenu.rename": "Preimenuj",
+ "page-menu.submenu.title": "Izbornik",
+ "page-menu.title": "Stranice",
+ "people-menu.change-color": "Promijeni boju",
+ "people-menu.change-name": "Promijeni ime",
+ "people-menu.follow": "Pratim",
+ "people-menu.following": "Pratim",
+ "people-menu.invite": "Pozovi druge",
+ "people-menu.leading": "Pratim te",
+ "people-menu.title": "Ljudi",
+ "people-menu.user": "(Ti)",
+ "rename-project-dialog.cancel": "Odustani",
+ "rename-project-dialog.rename": "Preimenuj",
+ "rename-project-dialog.title": "Preimenuj projekt",
+ "share-menu.copy-link": "Kopiraj vezu za dijeljenje",
+ "share-menu.copy-link-note": "Svatko s vezom moći će vidjeti i uređivati ovaj projekt.",
+ "share-menu.copy-readonly-link": "Kopiraj vezu samo za čitanje",
+ "share-menu.copy-readonly-link-note": "Svatko s vezom moći će vidjeti (ali ne i uređivati) ovaj projekt.",
+ "share-menu.create-snapshot-link": "Kopiraj vezu za snimak",
+ "share-menu.default-project-name": "Dijeljeni projekt",
+ "share-menu.fork-note": "Stvorite novi zajednički projekt na temelju ove snimke.",
+ "share-menu.offline-note": "Stvorite novi zajednički projekt na temelju vašeg trenutnog projekta.",
+ "share-menu.project-too-large": "Žao nam je, ovaj se projekt ne može dijeliti jer je prevelik. Radimo na tome!",
+ "share-menu.readonly-link": "Samo za čitanje",
+ "share-menu.save-note": "Preuzmite ovaj projekt na svoje računalo kao .tldr datoteku.",
+ "share-menu.share-project": "Podijelite ovaj projekt",
+ "share-menu.snapshot-link-note": "Snimite i podijelite ovaj projekt kao vezu za snimku samo za čitanje.",
+ "share-menu.title": "Dijeli",
+ "share-menu.upload-failed": "Žao nam je, trenutačno ne možemo prenijeti vaš projekt. Molimo pokušajte ponovno ili nam javite ako problem potraje.",
+ "sharing.confirm-leave.cancel": "Odustani",
+ "sharing.confirm-leave.description": "Jeste li sigurni da želite napustiti ovaj zajednički projekt? Možete mu se vratiti tako da odete na njegov URL.",
+ "sharing.confirm-leave.dont-show-again": "Ne pitaj više",
+ "sharing.confirm-leave.leave": "Napusti",
+ "sharing.confirm-leave.title": "Napustiti trenutni projekt?",
+ "shortcuts-dialog.collaboration": "Suradnja",
+ "shortcuts-dialog.edit": "Uredi",
+ "shortcuts-dialog.file": "Datoteka",
+ "shortcuts-dialog.preferences": "Postavke",
+ "shortcuts-dialog.title": "Tipkovne prečice",
+ "shortcuts-dialog.tools": "Alati",
+ "shortcuts-dialog.transform": "Transformacija",
+ "shortcuts-dialog.view": "Prikaz",
+ "size-style.l": "Velika",
+ "size-style.m": "Srednja",
+ "size-style.s": "Mala",
+ "size-style.xl": "Jako velika",
+ "spline-style.cubic": "Kockasti",
+ "spline-style.line": "Linija",
+ "status.offline": "Izvan mreže",
+ "status.online": "Na mreži",
+ "style-panel.align": "Poravnaj",
+ "style-panel.arrowhead-end": "Kraj",
+ "style-panel.arrowhead-start": "Početak",
+ "style-panel.arrowheads": "Strelice",
+ "style-panel.color": "Boja",
+ "style-panel.dash": "Crtica",
+ "style-panel.fill": "Ispuna",
+ "style-panel.font": "Font",
+ "style-panel.geo": "Oblik",
+ "style-panel.mixed": "Mješovito",
+ "style-panel.opacity": "Neprozirnost",
+ "style-panel.position": "Pozicija",
+ "style-panel.size": "Veličina",
+ "style-panel.spline": "Spline",
+ "style-panel.title": "Stilovi",
+ "style-panel.vertical-align": "Poravnaj okomito",
+ "toast.close": "Zatvori",
+ "toast.error.copy-fail.desc": "Neuspješno kopiranje slike",
+ "toast.error.copy-fail.title": "Neuspješna kopija",
+ "toast.error.export-fail.desc": "Izvoz slike nije uspio",
+ "toast.error.export-fail.title": "Izvoz nije uspio",
+ "tool-panel.drawing": "Crtež",
+ "tool-panel.more": "Više",
+ "tool-panel.shapes": "Oblici",
+ "tool.arrow": "Strelica",
+ "tool.arrow-down": "Strelica prema dolje",
+ "tool.arrow-left": "Strelica lijevo",
+ "tool.arrow-right": "Strelica desno",
+ "tool.arrow-up": "Strelica prema gore",
+ "tool.asset": "Sredstvo",
+ "tool.check-box": "Potvrdni okvir",
+ "tool.cloud": "Oblak",
+ "tool.diamond": "Dijamant",
+ "tool.draw": "Crtanje",
+ "tool.ellipse": "Elipsa",
+ "tool.embed": "Ugradi",
+ "tool.eraser": "Brisač",
+ "tool.frame": "Okvir",
+ "tool.hand": "Ruka",
+ "tool.hexagon": "Šesterokut",
+ "tool.highlight": "Istakni",
+ "tool.laser": "Laser",
+ "tool.line": "Linija",
+ "tool.note": "Bilješka",
+ "tool.octagon": "Osmerokut",
+ "tool.oval": "Oval",
+ "tool.pentagon": "Petokut",
+ "tool.rectangle": "Pravokutnik",
+ "tool.rhombus": "Romb",
+ "tool.select": "Odaberi",
+ "tool.star": "Zvijezda",
+ "tool.text": "Tekst",
+ "tool.trapezoid": "Trapez",
+ "tool.triangle": "Trokut",
+ "tool.x-box": "X kutija",
+ "verticalAlign-style.end": "Dno",
+ "verticalAlign-style.middle": "Sredina",
+ "verticalAlign-style.start": "Vrh",
+ "vscode.file-open.backup": "Sigurnosna kopija",
+ "vscode.file-open.backup-failed": "Sigurnosna kopija nije uspjela: ovo nije .tldr datoteka.",
+ "vscode.file-open.backup-saved": "Sigurnosna kopija spremljena",
+ "vscode.file-open.desc": "Ažurirali smo ovaj dokument da radi s trenutnom verzijom tldraw-a. Ako želite zadržati izvornu verziju (koja će raditi na old.tldraw.com), kliknite ispod za izradu sigurnosne kopije.",
+ "vscode.file-open.dont-show-again": "Ne pitaj više",
+ "vscode.file-open.open": "Nastavi"
+}
diff --git a/apps/web/public/translations/hu.json b/apps/web/public/translations/hu.json
new file mode 100644
index 00000000..11735023
--- /dev/null
+++ b/apps/web/public/translations/hu.json
@@ -0,0 +1,365 @@
+{
+ "action.align-bottom": "Lefelé igazítás",
+ "action.align-center-horizontal": "Vízszintes igazítás",
+ "action.align-center-horizontal.short": "V. igazítás",
+ "action.align-center-vertical": "Függőleges igazítás",
+ "action.align-center-vertical.short": "F. igazítás",
+ "action.align-left": "Balra igazítás",
+ "action.align-right": "Jobbra igazítás",
+ "action.align-top": "Felfelé igazítás",
+ "action.back-to-content": "Vissza a tartalomhoz",
+ "action.bring-forward": "Küldés előrébb",
+ "action.bring-to-front": "Küldés előre",
+ "action.convert-to-bookmark": "Konvertálás könyvjelzővé",
+ "action.convert-to-embed": "Konvertálás beágyazássá",
+ "action.copy": "Másolás",
+ "action.copy-as-json": "Másolás JSON-ként",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "Másolás PNG-ként",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "Másolás SVG-ként",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "Kivágás",
+ "action.delete": "Törlés",
+ "action.distribute-horizontal": "Vízszintes elosztás",
+ "action.distribute-horizontal.short": "V. elosztás",
+ "action.distribute-vertical": "Függőleges elosztás",
+ "action.distribute-vertical.short": "F. elosztás",
+ "action.duplicate": "Duplikálás",
+ "action.edit-link": "Hivatkozás szerkesztése",
+ "action.exit-pen-mode": "Kilépés a toll módból",
+ "action.export-all-as-json.short": "JSON",
+ "action.export-all-as-png.short": "PNG",
+ "action.export-all-as-svg.short": "SVG",
+ "action.export-as-json": "Exportálás JSON-ként",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "Exportálás PNG-ként",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "Exportálás SVG-ként",
+ "action.export-as-svg.short": "SVG",
+ "action.fit-frame-to-content": "Tartalomhoz igazítás",
+ "action.flip-horizontal": "Vízszintes tükrözés",
+ "action.flip-horizontal.short": "V. tükrözés",
+ "action.flip-vertical": "Függőleges tükrözés",
+ "action.flip-vertical.short": "F. tükrözés",
+ "action.fork-project": "Klónozd le ezt a projektet",
+ "action.group": "Csoportosítás",
+ "action.insert-embed": "Beágyazás beszúrása",
+ "action.insert-media": "Média feltöltése",
+ "action.leave-shared-project": "Kilépés a megosztott projektből",
+ "action.new-project": "Új projekt",
+ "action.new-shared-project": "Új megosztott projekt",
+ "action.open-cursor-chat": "Kurzor csevegés",
+ "action.open-embed-link": "Link megnyitása",
+ "action.open-file": "Fájl megnyitása",
+ "action.pack": "Csomagolás",
+ "action.paste": "Beillesztés",
+ "action.print": "Nyomtatás",
+ "action.redo": "Újra",
+ "action.remove-frame": "Keret eltávolítása",
+ "action.rename": "Átnevezés",
+ "action.rotate-ccw": "Forgatás az óramutató ellen",
+ "action.rotate-cw": "Forgatás az óramutató szerint",
+ "action.save-copy": "Másolat mentése",
+ "action.select-all": "Az összes kijelölése",
+ "action.select-none": "Kijelölés törlése",
+ "action.send-backward": "Küldés hátrébb",
+ "action.send-to-back": "Küldés hátra",
+ "action.share-project": "A projekt megosztása",
+ "action.stack-horizontal": "Vízszintes halmozás",
+ "action.stack-horizontal.short": "V. halmozás",
+ "action.stack-vertical": "Függőleges halmozás",
+ "action.stack-vertical.short": "F. halmozás",
+ "action.stop-following": "Követés leállítása",
+ "action.stretch-horizontal": "Vízszintes nyújtás",
+ "action.stretch-horizontal.short": "V. nyújtás",
+ "action.stretch-vertical": "Függőleges nyújtás",
+ "action.stretch-vertical.short": "F. nyújtás",
+ "action.toggle-auto-size": "Automatikus méret be- és kikapcsolása",
+ "action.toggle-dark-mode": "Sötét mód be- és kikapcsolása",
+ "action.toggle-dark-mode.menu": "Sötét mód",
+ "action.toggle-debug-mode": "Hibakeresési mód be- és kikapcsolása",
+ "action.toggle-debug-mode.menu": "Hibakeresési mód",
+ "action.toggle-edge-scrolling": "A szélgörgetés be- és kikapcsolása",
+ "action.toggle-edge-scrolling.menu": "Szélgörgetés",
+ "action.toggle-focus-mode": "Fókusz mód be- és kikapcsolása",
+ "action.toggle-focus-mode.menu": "Fókusz mód",
+ "action.toggle-grid": "Rács be- és kikapcsolása",
+ "action.toggle-grid.menu": "Rács megjelenítése",
+ "action.toggle-lock": "Zárolás / Feloldás",
+ "action.toggle-reduce-motion": "Mozgáscsökkentés be- és kikapcsolása",
+ "action.toggle-reduce-motion.menu": "Mozgás csökkentése",
+ "action.toggle-snap-mode": "Igazodó mód be- és kikapcsolása",
+ "action.toggle-snap-mode.menu": "Mindig igazodjon",
+ "action.toggle-tool-lock": "Szerszámzár be- és kikapcsolása",
+ "action.toggle-tool-lock.menu": "Szerszámzár",
+ "action.toggle-transparent": "Átlátszó háttér be- és kikapcsolása",
+ "action.toggle-transparent.context-menu": "Átlátszó",
+ "action.toggle-transparent.menu": "Átlátszó",
+ "action.undo": "Visszavonás",
+ "action.ungroup": "Csoportosítás feloldása",
+ "action.unlock-all": "Az összes feloldása",
+ "action.zoom-in": "Nagyítás",
+ "action.zoom-out": "Kicsinyítés",
+ "action.zoom-to-100": "Nagyítás 100%-ra",
+ "action.zoom-to-fit": "Nagyítás, hogy illeszkedjen",
+ "action.zoom-to-selection": "Nagyítás a kijelöléshez",
+ "actions-menu.title": "Műveletek",
+ "align-style.end": "Végére",
+ "align-style.justify": "Sorkizárt",
+ "align-style.middle": "Középre",
+ "align-style.start": "Elejére",
+ "arrowheadEnd-style.arrow": "Nyíl",
+ "arrowheadEnd-style.bar": "Rúd",
+ "arrowheadEnd-style.diamond": "Gyémánt",
+ "arrowheadEnd-style.dot": "Pont",
+ "arrowheadEnd-style.inverted": "Fordított",
+ "arrowheadEnd-style.none": "Egyik sem",
+ "arrowheadEnd-style.pipe": "Cső",
+ "arrowheadEnd-style.square": "Négyzet",
+ "arrowheadEnd-style.triangle": "Háromszög",
+ "arrowheadStart-style.arrow": "Nyíl",
+ "arrowheadStart-style.bar": "Rúd",
+ "arrowheadStart-style.diamond": "Gyémánt",
+ "arrowheadStart-style.dot": "Pont",
+ "arrowheadStart-style.inverted": "Fordított",
+ "arrowheadStart-style.none": "Egyik sem",
+ "arrowheadStart-style.pipe": "Cső",
+ "arrowheadStart-style.square": "Négyzet",
+ "arrowheadStart-style.triangle": "Háromszög",
+ "assets.files.upload-failed": "Feltöltés sikertelen",
+ "assets.url.failed": "Nem sikerült betölteni az URL előnézetét",
+ "color-style.black": "Fekete",
+ "color-style.blue": "Kék",
+ "color-style.green": "Zöld",
+ "color-style.grey": "Szürke",
+ "color-style.light-blue": "Világoskék",
+ "color-style.light-green": "Világoszöld",
+ "color-style.light-red": "Világos piros",
+ "color-style.light-violet": "Világos ibolya",
+ "color-style.orange": "Narancssárga",
+ "color-style.red": "Piros",
+ "color-style.violet": "Ibolya",
+ "color-style.yellow": "Sárga",
+ "context-menu.arrange": "Rendezés",
+ "context-menu.copy-as": "Másolás mint",
+ "context-menu.export-as": "Exportálás mint",
+ "context-menu.move-to-page": "Mozgatás az oldalra",
+ "context-menu.reorder": "Újrarendelés",
+ "context.pages.new-page": "Új oldal",
+ "cursor-chat.type-to-chat": "Írj a csevegéshez...",
+ "dash-style.dashed": "Szaggatott",
+ "dash-style.dotted": "Pontozott",
+ "dash-style.draw": "Rajzolt",
+ "dash-style.solid": "Teli",
+ "debug-panel.more": "Több",
+ "document.default-name": "Névtelen",
+ "edit-link-dialog.cancel": "Mégse",
+ "edit-link-dialog.clear": "Törlés",
+ "edit-link-dialog.detail": "A hivatkozások új lapon nyílnak meg.",
+ "edit-link-dialog.invalid-url": "A hivatkozásnak érvényes URL-címnek kell lennie.",
+ "edit-link-dialog.save": "Folytatás",
+ "edit-link-dialog.title": "Hivatkozás szerkesztése",
+ "edit-link-dialog.url": "URL cím",
+ "edit-pages-dialog.move-down": "Mozgatás lefelé",
+ "edit-pages-dialog.move-up": "Mozgatás felfelé",
+ "embed-dialog.back": "Vissza",
+ "embed-dialog.cancel": "Mégse",
+ "embed-dialog.create": "Létrehozás",
+ "embed-dialog.instruction": "Illeszd be a weboldal URL-jét, hogy létrehozd a beágyazást.",
+ "embed-dialog.invalid-url": "Nem tudtunk beágyazást létrehozni ebből az URL-címből.",
+ "embed-dialog.title": "Beágyazás létrehozása",
+ "embed-dialog.url": "URL cím",
+ "file-system.confirm-clear.cancel": "Mégse",
+ "file-system.confirm-clear.continue": "Folytatás",
+ "file-system.confirm-clear.description": "Új projekt létrehozása törli az aktuális projektet, és a nem mentett módosítások elvesznek. Biztos vagy benne, hogy folytatni akarod?",
+ "file-system.confirm-clear.dont-show-again": "Ne kérdezd többet",
+ "file-system.confirm-clear.title": "Töröljük a jelenlegi projektet?",
+ "file-system.confirm-open.cancel": "Mégse",
+ "file-system.confirm-open.description": "A fájl megnyitása lecseréli az aktuális projektet, és a nem mentett módosítások elvesznek. Biztos vagy benne, hogy folytatni akarod?",
+ "file-system.confirm-open.dont-show-again": "Ne kérdezd többet",
+ "file-system.confirm-open.open": "Fájl megnyitása",
+ "file-system.confirm-open.title": "Felülírja az aktuális projektet?",
+ "file-system.file-open-error.file-format-version-too-new": "A fájlt, amit megpróbáltál megnyitni, egy újabb tldraw verzióból származik. Kérlek, töltsd újra az oldalt és próbáld meg újra.",
+ "file-system.file-open-error.generic-corrupted-file": "A megnyitni próbált fájl sérült.",
+ "file-system.file-open-error.not-a-tldraw-file": "A megnyitni próbált fájl nem úgy néz ki, mint egy tldraw fájl.",
+ "file-system.file-open-error.title": "Nem sikerült megnyitni a fájlt",
+ "file-system.shared-document-file-open-error.description": "A megosztott projektekből származó fájlok megnyitása nem támogatott.",
+ "file-system.shared-document-file-open-error.title": "Nem sikerült megnyitni a fájlt",
+ "fill-style.none": "Nincs",
+ "fill-style.pattern": "Mintázat",
+ "fill-style.semi": "Félig",
+ "fill-style.solid": "Teli",
+ "focus-mode.toggle-focus-mode": "Fókusz mód be- és kikapcsolása",
+ "font-style.draw": "Rajzolás",
+ "font-style.mono": "Mono",
+ "font-style.sans": "Sans",
+ "font-style.serif": "Serif",
+ "geo-style.arrow-down": "Nyíl lefelé",
+ "geo-style.arrow-left": "Nyíl balra",
+ "geo-style.arrow-right": "Nyíl jobbra",
+ "geo-style.arrow-up": "Nyíl felfelé",
+ "geo-style.check-box": "Jelölőnégyzet",
+ "geo-style.cloud": "Felhő",
+ "geo-style.diamond": "Gyémánt",
+ "geo-style.ellipse": "Ellipszis",
+ "geo-style.hexagon": "Hatszög",
+ "geo-style.octagon": "Nyolcszög",
+ "geo-style.oval": "Ovális",
+ "geo-style.pentagon": "Ötszög",
+ "geo-style.rectangle": "Téglalap",
+ "geo-style.rhombus": "Rombusz",
+ "geo-style.rhombus-2": "Rombusz 2",
+ "geo-style.star": "Csillag",
+ "geo-style.trapezoid": "Trapéz",
+ "geo-style.triangle": "Háromszög",
+ "geo-style.x-box": "X doboz",
+ "help-menu.about": "Rólunk",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "Billentyűparancsok",
+ "help-menu.title": "Segítség és források",
+ "help-menu.twitter": "Twitter",
+ "home-project-dialog.description": "Ez a te helyi otthoni projekted. Csak neked szól!",
+ "home-project-dialog.ok": "Oké",
+ "home-project-dialog.title": "Otthoni projekt",
+ "menu.copy-as": "Másolás mint",
+ "menu.edit": "Szerkesztés",
+ "menu.export-as": "Exportálás mint",
+ "menu.file": "Fájl",
+ "menu.language": "Nyelv",
+ "menu.preferences": "Beállítások",
+ "menu.title": "Menü",
+ "menu.view": "Megtekintés",
+ "navigation-zone.toggle-minimap": "Minitérkép be- és kikapcsolása",
+ "navigation-zone.zoom": "Nagyítás",
+ "opacity-style.0.1": "10%",
+ "opacity-style.0.25": "25%",
+ "opacity-style.0.5": "50%",
+ "opacity-style.0.75": "75%",
+ "opacity-style.1": "100%",
+ "page-menu.create-new-page": "Új oldal létrehozása",
+ "page-menu.edit-done": "Kész",
+ "page-menu.edit-start": "Szerkesztés",
+ "page-menu.go-to-page": "Ugrás oldalra",
+ "page-menu.max-page-count-reached": "Maximális oldalszám elérve",
+ "page-menu.new-page-initial-name": "1. oldal",
+ "page-menu.submenu.delete": "Törlés",
+ "page-menu.submenu.duplicate-page": "Duplikálás",
+ "page-menu.submenu.move-down": "Mozgatás lefelé",
+ "page-menu.submenu.move-up": "Mozgatás felfelé",
+ "page-menu.submenu.rename": "Átnevezés",
+ "page-menu.submenu.title": "Menü",
+ "page-menu.title": "Oldalak",
+ "people-menu.change-color": "Szín módosítása",
+ "people-menu.change-name": "Név módosítása",
+ "people-menu.follow": "Követés",
+ "people-menu.following": "Követve",
+ "people-menu.invite": "Hívj meg másokat is",
+ "people-menu.leading": "Követnek",
+ "people-menu.title": "Emberek",
+ "people-menu.user": "(Te)",
+ "rename-project-dialog.cancel": "Mégse",
+ "rename-project-dialog.rename": "Átnevezés",
+ "rename-project-dialog.title": "Projekt átnevezése",
+ "share-menu.copy-link": "Hivatkozás másolása",
+ "share-menu.copy-link-note": "A link birtokában bárki megtekintheti és szerkesztheti ezt a projektet.",
+ "share-menu.copy-readonly-link": "Csak olvasható hivatkozás másolása",
+ "share-menu.copy-readonly-link-note": "A link birtokában bárki megtekintheti (de nem szerkesztheti) ezt a projektet.",
+ "share-menu.create-snapshot-link": "Pillanatkép-hivatkozás létrehozása",
+ "share-menu.default-project-name": "Megosztott projekt",
+ "share-menu.fork-note": "Hozz létre egy új, megosztott projektet ebből a pillanatképből.",
+ "share-menu.offline-note": "Ha megosztod ezt a projektet, egy új URL-en létrejön egy élő, hosztolt másolat. Azt az URL-t akár harminc másik emberrel is megoszthatod, hogy együtt nézhessék és szerkesszék a projektet.",
+ "share-menu.project-too-large": "Sajnáljuk, ezt a projektet nem lehet megosztani, mert túl nagy. Dolgozunk rajta!",
+ "share-menu.readonly-link": "Csak olvasható",
+ "share-menu.save-note": "Töltsd le ezt a projektet a gépedre .tldr fájlként.",
+ "share-menu.share-project": "A projekt megosztása",
+ "share-menu.snapshot-link-note": "Rögzítsd és oszd meg ezt a projektet olvasásra korlátozott pillanatkép linkként.",
+ "share-menu.title": "Megosztás",
+ "share-menu.upload-failed": "Sajnáljuk, most nem tudtuk feltölteni a projektet. Kérlek, próbáld újra, vagy szólj nekünk, ha a probléma továbbra is fennáll.",
+ "sharing.confirm-leave.cancel": "Mégse",
+ "sharing.confirm-leave.description": "Biztosan ki akarsz lépni ebből a megosztott projektből? A projekt URL-jére kattintva bármikor visszatérhetsz.",
+ "sharing.confirm-leave.dont-show-again": "Ne kérdezd többet",
+ "sharing.confirm-leave.leave": "Kilép",
+ "sharing.confirm-leave.title": "Kilépsz a jelenlegi projektből?",
+ "shortcuts-dialog.collaboration": "Együttműködés",
+ "shortcuts-dialog.edit": "Szerkesztés",
+ "shortcuts-dialog.file": "Fájl",
+ "shortcuts-dialog.preferences": "Beállítások",
+ "shortcuts-dialog.title": "Billentyűparancsok",
+ "shortcuts-dialog.tools": "Eszközök",
+ "shortcuts-dialog.transform": "Átalakítás",
+ "shortcuts-dialog.view": "Megtekintés",
+ "size-style.l": "Nagy",
+ "size-style.m": "Közepes",
+ "size-style.s": "Kicsi",
+ "size-style.xl": "Extra nagy",
+ "spline-style.cubic": "Kocka alakú",
+ "spline-style.line": "Vonal",
+ "status.offline": "Offline",
+ "status.online": "Online",
+ "style-panel.align": "Igazítás",
+ "style-panel.arrowhead-end": "Vége",
+ "style-panel.arrowhead-start": "Eleje",
+ "style-panel.arrowheads": "Nyílhegyek",
+ "style-panel.color": "Szín",
+ "style-panel.dash": "Körvonal",
+ "style-panel.fill": "Kitöltés",
+ "style-panel.font": "Betűtípus",
+ "style-panel.geo": "Alakzat",
+ "style-panel.mixed": "Vegyes",
+ "style-panel.opacity": "Átlátszatlanság",
+ "style-panel.position": "Pozíció",
+ "style-panel.size": "Méret",
+ "style-panel.spline": "Spline",
+ "style-panel.title": "Stílusok",
+ "style-panel.vertical-align": "Függőleges igazítás",
+ "toast.close": "Bezár",
+ "toast.error.copy-fail.desc": "Nem sikerült másolni a képet",
+ "toast.error.copy-fail.title": "Sikertelen másolás",
+ "toast.error.export-fail.desc": "Nem sikerült exportálni a képet",
+ "toast.error.export-fail.title": "Sikertelen exportálás",
+ "tool-panel.drawing": "Rajzolás",
+ "tool-panel.more": "Több",
+ "tool-panel.shapes": "Alakzatok",
+ "tool.arrow": "Nyíl",
+ "tool.arrow-down": "Nyíl lefelé",
+ "tool.arrow-left": "Nyíl balra",
+ "tool.arrow-right": "Nyíl jobbra",
+ "tool.arrow-up": "Nyíl felfelé",
+ "tool.asset": "Eszköz",
+ "tool.check-box": "Jelölőnégyzet",
+ "tool.cloud": "Felhő",
+ "tool.diamond": "Gyémánt",
+ "tool.draw": "Rajzolás",
+ "tool.ellipse": "Ellipszis",
+ "tool.embed": "Beágyazás",
+ "tool.eraser": "Radír",
+ "tool.frame": "Keret",
+ "tool.hand": "Kéz",
+ "tool.hexagon": "Hatszög",
+ "tool.highlight": "Kiemelés",
+ "tool.laser": "Lézer",
+ "tool.line": "Vonal",
+ "tool.note": "Jegyzet",
+ "tool.octagon": "Nyolcszög",
+ "tool.oval": "Ovális",
+ "tool.pentagon": "Ötszög",
+ "tool.rectangle": "Téglalap",
+ "tool.rhombus": "Rombusz",
+ "tool.select": "Kiválasztás",
+ "tool.star": "Csillag",
+ "tool.text": "Szöveg",
+ "tool.trapezoid": "Trapéz",
+ "tool.triangle": "Háromszög",
+ "tool.x-box": "X doboz",
+ "verticalAlign-style.end": "Alulra",
+ "verticalAlign-style.middle": "Középre",
+ "verticalAlign-style.start": "Felülre",
+ "vscode.file-open.backup": "Biztonsági mentés",
+ "vscode.file-open.backup-failed": "A biztonsági mentés nem sikerült: ez nem .tldr fájl.",
+ "vscode.file-open.backup-saved": "Biztonsági másolat mentve",
+ "vscode.file-open.desc": "Ez a fájl egy előző verzióval készült. Frissítsük, hogy az új verzióval is működjön?",
+ "vscode.file-open.dont-show-again": "Ne kérdezd többet",
+ "vscode.file-open.open": "Folytatás"
+}
diff --git a/apps/web/public/translations/id.json b/apps/web/public/translations/id.json
new file mode 100644
index 00000000..00ef5a52
--- /dev/null
+++ b/apps/web/public/translations/id.json
@@ -0,0 +1,373 @@
+{
+ "action.align-bottom": "Sejajar Bawah",
+ "action.align-center-horizontal": "Sejajar horizontal",
+ "action.align-center-horizontal.short": "Sejajar H",
+ "action.align-center-vertical": "Sejajar vertikal",
+ "action.align-center-vertical.short": "Sejajar V",
+ "action.align-left": "Rata kiri",
+ "action.align-right": "Rata kanan",
+ "action.align-top": "Rata atas",
+ "action.back-to-content": "Kembali ke konten",
+ "action.bring-forward": "Maju selapis",
+ "action.bring-to-front": "Maju paling depan",
+ "action.convert-to-bookmark": "Konversikan ke Penanda",
+ "action.convert-to-embed": "Konversi ke Sematkan",
+ "action.copy": "Salin",
+ "action.copy-as-json": "Salin sebagai JSON",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "Salin sebagai PNG",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "Salin sebagai SVG",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "Potong",
+ "action.delete": "Hapus",
+ "action.distribute-horizontal": "Ratakan horizontal",
+ "action.distribute-horizontal.short": "Ratakan H",
+ "action.distribute-vertical": "Ratakan vertikal",
+ "action.distribute-vertical.short": "Ratakan V",
+ "action.duplicate": "Duplikat",
+ "action.edit-link": "Sunting tautan",
+ "action.exit-pen-mode": "Keluar dari mode pena",
+ "action.export-all-as-json": "Ekspor semua sebagai JSON",
+ "action.export-all-as-json.short": "JSON",
+ "action.export-all-as-png": "Ekspor semua sebagai PNG",
+ "action.export-all-as-png.short": "PNG",
+ "action.export-all-as-svg": "Ekspor semua sebagai SVG",
+ "action.export-all-as-svg.short": "SVG",
+ "action.export-as-json": "Ekspor sebagai JSON",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "Ekspor sebagai PNG",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "Ekspor sebagai SVG",
+ "action.export-as-svg.short": "SVG",
+ "action.fit-frame-to-content": "Sesuai dengan konten",
+ "action.flip-horizontal": "Balik horizontal",
+ "action.flip-horizontal.short": "Balik H",
+ "action.flip-vertical": "Balik vertikal",
+ "action.flip-vertical.short": "Balik V",
+ "action.fork-project": "Fork proyek ini",
+ "action.fork-project-on-tldraw": "Fork projek di tldraw",
+ "action.group": "Grup",
+ "action.insert-embed": "Sisipkan sematan",
+ "action.insert-media": "Unggah media",
+ "action.leave-shared-project": "Tinggalkan proyek bersama",
+ "action.new-project": "Proyek baru",
+ "action.new-shared-project": "Proyek bersama baru",
+ "action.open-cursor-chat": "Obrolan kursor",
+ "action.open-embed-link": "Buka tautan",
+ "action.open-file": "Buka berkas",
+ "action.pack": "Bungkus",
+ "action.paste": "Tempel",
+ "action.print": "Cetak",
+ "action.redo": "Redo",
+ "action.remove-frame": "Hapus bingkai",
+ "action.rename": "Ubah nama",
+ "action.rotate-ccw": "Putar lawan arah jarum jam",
+ "action.rotate-cw": "Putar searah jarum jam",
+ "action.save-copy": "Simpan salinan",
+ "action.select-all": "Pilih semua",
+ "action.select-none": "Pilih kosong",
+ "action.send-backward": "Mundur selapis",
+ "action.send-to-back": "Mundur paling belakang",
+ "action.share-project": "Bagikan proyek ini",
+ "action.stack-horizontal": "Tumpuk Horizontal",
+ "action.stack-horizontal.short": "Tumpuk H",
+ "action.stack-vertical": "Tumpuk Vertikal",
+ "action.stack-vertical.short": "Tumpuk V",
+ "action.stop-following": "Berhenti mengikuti",
+ "action.stretch-horizontal": "Regangkan Horizontal",
+ "action.stretch-horizontal.short": "Regangkan H",
+ "action.stretch-vertical": "Regangkan Vertikal",
+ "action.stretch-vertical.short": "Regangkan V",
+ "action.toggle-auto-size": "Alihkan ukuran otomatis",
+ "action.toggle-dark-mode": "Alihkan mode gelap",
+ "action.toggle-dark-mode.menu": "Mode gelap",
+ "action.toggle-debug-mode": "Alihkan mode debug",
+ "action.toggle-debug-mode.menu": "Mode debug",
+ "action.toggle-edge-scrolling": "Alihkan gulir tepi",
+ "action.toggle-edge-scrolling.menu": "Gulir tepi",
+ "action.toggle-focus-mode": "Alihkan mode fokus",
+ "action.toggle-focus-mode.menu": "Mode fokus",
+ "action.toggle-grid": "Alihkan grid",
+ "action.toggle-grid.menu": "Tampilkan grid",
+ "action.toggle-lock": "Kunci / Buka Kunci",
+ "action.toggle-reduce-motion": "Aktifkan kurangi gerakan",
+ "action.toggle-reduce-motion.menu": "Kurangi gerakan",
+ "action.toggle-snap-mode": "Beralih selalu snap",
+ "action.toggle-snap-mode.menu": "Selalu snap",
+ "action.toggle-tool-lock": "Alihkan kunci alat",
+ "action.toggle-tool-lock.menu": "Kunci alat",
+ "action.toggle-transparent": "Alihkan latar belakang transparan",
+ "action.toggle-transparent.context-menu": "Transparan",
+ "action.toggle-transparent.menu": "Transparan",
+ "action.toggle-wrap-mode": "Aktifkan Pilih saat bungkus",
+ "action.toggle-wrap-mode.menu": "Pilih saat bungkus",
+ "action.undo": "Undo",
+ "action.ungroup": "Pisahkan",
+ "action.unlock-all": "Buka kunci semua",
+ "action.zoom-in": "Perbesar",
+ "action.zoom-out": "Perkecil",
+ "action.zoom-to-100": "Perbesar hingga 100%",
+ "action.zoom-to-fit": "Perbesar agar pas",
+ "action.zoom-to-selection": "Perbesar ke pilihan",
+ "actions-menu.title": "Aksi",
+ "align-style.end": "Akhir",
+ "align-style.justify": "Rata kanan-kiri",
+ "align-style.middle": "Tengah",
+ "align-style.start": "Awal",
+ "arrowheadEnd-style.arrow": "Panah",
+ "arrowheadEnd-style.bar": "Batang",
+ "arrowheadEnd-style.diamond": "Belah ketupat",
+ "arrowheadEnd-style.dot": "Titik",
+ "arrowheadEnd-style.inverted": "Terbalik",
+ "arrowheadEnd-style.none": "Tidak ada",
+ "arrowheadEnd-style.pipe": "Pipa",
+ "arrowheadEnd-style.square": "Persegi",
+ "arrowheadEnd-style.triangle": "Segitiga",
+ "arrowheadStart-style.arrow": "Panah",
+ "arrowheadStart-style.bar": "Batang",
+ "arrowheadStart-style.diamond": "Belah ketupat",
+ "arrowheadStart-style.dot": "Titik",
+ "arrowheadStart-style.inverted": "Terbalik",
+ "arrowheadStart-style.none": "Tidak ada",
+ "arrowheadStart-style.pipe": "Pipa",
+ "arrowheadStart-style.square": "Persegi",
+ "arrowheadStart-style.triangle": "Segitiga",
+ "assets.files.upload-failed": "Gagal unggah",
+ "assets.url.failed": "Tidak dapat memuat pratinjau URL",
+ "color-style.black": "Hitam",
+ "color-style.blue": "Biru",
+ "color-style.green": "Hijau",
+ "color-style.grey": "Abu-abu",
+ "color-style.light-blue": "Biru muda",
+ "color-style.light-green": "Hijau muda",
+ "color-style.light-red": "Merah muda",
+ "color-style.light-violet": "Ungu muda",
+ "color-style.orange": "Oranye",
+ "color-style.red": "Merah",
+ "color-style.violet": "Ungu",
+ "color-style.white": "Putih",
+ "color-style.yellow": "Kuning",
+ "context-menu.arrange": "Atur",
+ "context-menu.copy-as": "Salin sebagai",
+ "context-menu.export-all-as": "Ekspor semua sebagai",
+ "context-menu.export-as": "Ekspor sebagai",
+ "context-menu.move-to-page": "Pindah ke halaman",
+ "context-menu.reorder": "Susun ulang",
+ "context.pages.new-page": "Halaman baru",
+ "cursor-chat.type-to-chat": "Ketik untuk mengobrol...",
+ "dash-style.dashed": "Putus-putus",
+ "dash-style.dotted": "Titik-titik",
+ "dash-style.draw": "Draw",
+ "dash-style.solid": "Solid",
+ "debug-panel.more": "Lanjut",
+ "document.default-name": "Tanpa Judul",
+ "edit-link-dialog.cancel": "Batal",
+ "edit-link-dialog.clear": "Kosongkan",
+ "edit-link-dialog.detail": "Tautan akan terbuka di tab baru.",
+ "edit-link-dialog.invalid-url": "Tautan harus berupa URL yang valid.",
+ "edit-link-dialog.save": "Lanjutkan",
+ "edit-link-dialog.title": "Sunting tautan",
+ "edit-link-dialog.url": "URL",
+ "edit-pages-dialog.move-down": "Turun",
+ "edit-pages-dialog.move-up": "Naik",
+ "embed-dialog.back": "Kembali",
+ "embed-dialog.cancel": "Batal",
+ "embed-dialog.create": "Membuat",
+ "embed-dialog.instruction": "Tempelkan URL situs untuk membuat penyematan.",
+ "embed-dialog.invalid-url": "Kami tidak dapat membuat sematan dari URL tersebut.",
+ "embed-dialog.title": "Buat penyematan",
+ "embed-dialog.url": "URL",
+ "file-system.confirm-clear.cancel": "Batal",
+ "file-system.confirm-clear.continue": "Lanjutkan",
+ "file-system.confirm-clear.description": "Membuat proyek baru akan menghapus proyek Anda saat ini dan semua perubahan yang belum disimpan akan hilang. Apakah Anda yakin ingin melanjutkan?",
+ "file-system.confirm-clear.dont-show-again": "Jangan tanya lagi",
+ "file-system.confirm-clear.title": "Hapus proyek saat ini?",
+ "file-system.confirm-open.cancel": "Batal",
+ "file-system.confirm-open.description": "Membuka proyek baru akan menggantikan proyek Anda saat ini dan semua perubahan yang belum disimpan akan hilang. Apakah Anda yakin ingin melanjutkan?",
+ "file-system.confirm-open.dont-show-again": "Jangan tanya lagi",
+ "file-system.confirm-open.open": "Buka berkas",
+ "file-system.confirm-open.title": "Timpa proyek saat ini?",
+ "file-system.file-open-error.file-format-version-too-new": "File yang Anda coba buka berasal dari tldraw versi terbaru. Silakan muat ulang halaman dan coba lagi.",
+ "file-system.file-open-error.generic-corrupted-file": "File yang Anda coba buka rusak.",
+ "file-system.file-open-error.not-a-tldraw-file": "File yang Anda coba buka tidak terlihat seperti file tldraw.",
+ "file-system.file-open-error.title": "Tidak dapat membuka file",
+ "file-system.shared-document-file-open-error.description": "Membuka file dari proyek bersama tidak didukung.",
+ "file-system.shared-document-file-open-error.title": "Tidak dapat membuka file",
+ "fill-style.none": "Tidak ada",
+ "fill-style.pattern": "Pola",
+ "fill-style.semi": "Semi",
+ "fill-style.solid": "Solid",
+ "focus-mode.toggle-focus-mode": "Alihkan mode fokus",
+ "font-style.draw": "Draw",
+ "font-style.mono": "Mono",
+ "font-style.sans": "Sans",
+ "font-style.serif": "Serif",
+ "geo-style.arrow-down": "Panah bawah",
+ "geo-style.arrow-left": "Panah kiri",
+ "geo-style.arrow-right": "Panah kanan",
+ "geo-style.arrow-up": "Panah atas",
+ "geo-style.check-box": "Kotak centang",
+ "geo-style.cloud": "Awan",
+ "geo-style.diamond": "Belah ketupat",
+ "geo-style.ellipse": "Elips",
+ "geo-style.hexagon": "Segi enam",
+ "geo-style.octagon": "Segi delapan",
+ "geo-style.oval": "Oval",
+ "geo-style.pentagon": "Segi lima",
+ "geo-style.rectangle": "Persegi panjang",
+ "geo-style.rhombus": "Jajaran genjang",
+ "geo-style.rhombus-2": "Jajaran genjang 2",
+ "geo-style.star": "Bintang",
+ "geo-style.trapezoid": "Trapesium",
+ "geo-style.triangle": "Segitiga",
+ "geo-style.x-box": "Kotak X",
+ "help-menu.about": "Tentang",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "Pintasan papan ketik",
+ "help-menu.title": "Bantuan dan sumber daya",
+ "help-menu.twitter": "Twitter",
+ "home-project-dialog.description": "Ini adalah proyek rumah lokal Anda. Ini hanya untuk Anda!",
+ "home-project-dialog.ok": "Oke",
+ "home-project-dialog.title": "Proyek lokal",
+ "menu.copy-as": "Salin sebagai",
+ "menu.edit": "Sunting",
+ "menu.export-as": "Ekspor sebagai",
+ "menu.file": "Berkas",
+ "menu.language": "Bahasa",
+ "menu.preferences": "Preferensi",
+ "menu.title": "Menu",
+ "menu.view": "Lihat",
+ "navigation-zone.toggle-minimap": "Aktifkan minimap",
+ "navigation-zone.zoom": "Perbesar",
+ "opacity-style.0.1": "10%",
+ "opacity-style.0.25": "25%",
+ "opacity-style.0.5": "50%",
+ "opacity-style.0.75": "75%",
+ "opacity-style.1": "100%",
+ "page-menu.create-new-page": "Buat halaman baru",
+ "page-menu.edit-done": "Selesai",
+ "page-menu.edit-start": "Sunting",
+ "page-menu.go-to-page": "Pergi ke halaman",
+ "page-menu.max-page-count-reached": "Halaman maksimal tercapai",
+ "page-menu.new-page-initial-name": "Halaman 1",
+ "page-menu.submenu.delete": "Hapus",
+ "page-menu.submenu.duplicate-page": "Duplikat",
+ "page-menu.submenu.move-down": "Turun",
+ "page-menu.submenu.move-up": "Naik",
+ "page-menu.submenu.rename": "Ubah nama",
+ "page-menu.submenu.title": "Menu",
+ "page-menu.title": "Halaman",
+ "people-menu.change-color": "Ubah warna",
+ "people-menu.change-name": "Ubah nama",
+ "people-menu.follow": "Ikuti",
+ "people-menu.following": "Mengikuti",
+ "people-menu.invite": "Undang orang lain",
+ "people-menu.leading": "Mengikuti Anda",
+ "people-menu.title": "Orang",
+ "people-menu.user": "(Anda)",
+ "rename-project-dialog.cancel": "Batal",
+ "rename-project-dialog.rename": "Ubah nama",
+ "rename-project-dialog.title": "Ganti nama proyek",
+ "share-menu.copy-link": "Salin tautan",
+ "share-menu.copy-link-note": "Siapa pun yang memiliki tautan akan dapat melihat dan mengedit proyek ini.",
+ "share-menu.copy-readonly-link": "Salin tautan hanya-baca",
+ "share-menu.copy-readonly-link-note": "Siapa pun yang memiliki tautan akan dapat melihat (tetapi tidak dapat mengedit) proyek ini.",
+ "share-menu.create-snapshot-link": "Membuat tautan snapshot",
+ "share-menu.default-project-name": "Proyek Bersama",
+ "share-menu.fork-note": "Buat proyek bersama baru berdasarkan snapshot ini.",
+ "share-menu.offline-note": "Membagikan proyek ini akan membuat salinan langsung yang dihosting di URL baru. Anda dapat membagikan URL tersebut kepada hingga tiga puluh orang untuk melihat dan mengedit proyek bersama-sama.",
+ "share-menu.project-too-large": "Maaf, proyek ini tidak dapat dibagikan karena terlalu besar. Kami sedang mengerjakannya!",
+ "share-menu.readonly-link": "Hanya-baca",
+ "share-menu.save-note": "Unduh proyek ini ke komputer Anda sebagai file .tldr.",
+ "share-menu.share-project": "Bagikan proyek ini",
+ "share-menu.snapshot-link-note": "Tangkap dan bagikan proyek ini sebagai tautan snapshot baca-saja.",
+ "share-menu.title": "Bagikan",
+ "share-menu.upload-failed": "Maaf, kami tidak dapat mengunggah proyek Anda saat ini. Silakan coba lagi atau beri tahu kami jika masalah berlanjut.",
+ "sharing.confirm-leave.cancel": "Batal",
+ "sharing.confirm-leave.description": "Apakah Anda yakin ingin keluar dari proyek bersama ini? Anda dapat kembali ke sana dengan menavigasi ke URL-nya.",
+ "sharing.confirm-leave.dont-show-again": "Jangan tanya lagi",
+ "sharing.confirm-leave.leave": "Keluar",
+ "sharing.confirm-leave.title": "Keluar dari proyek saat ini?",
+ "shortcuts-dialog.collaboration": "Kolaborasi",
+ "shortcuts-dialog.edit": "Sunting",
+ "shortcuts-dialog.file": "Berkas",
+ "shortcuts-dialog.preferences": "Preferensi",
+ "shortcuts-dialog.title": "Pintasan papan ketik",
+ "shortcuts-dialog.tools": "Peralatan",
+ "shortcuts-dialog.transform": "Ubah",
+ "shortcuts-dialog.view": "Lihat",
+ "size-style.l": "Besar",
+ "size-style.m": "Sedang",
+ "size-style.s": "Kecil",
+ "size-style.xl": "Ekstra besar",
+ "spline-style.cubic": "Kubik",
+ "spline-style.line": "Garis",
+ "status.offline": "Offline",
+ "status.online": "Online",
+ "style-panel.align": "Sejajarkan",
+ "style-panel.arrowhead-end": "Akhir",
+ "style-panel.arrowhead-start": "Awal",
+ "style-panel.arrowheads": "Mata panah",
+ "style-panel.color": "Warna",
+ "style-panel.dash": "Dash",
+ "style-panel.fill": "Isi",
+ "style-panel.font": "Font",
+ "style-panel.geo": "Bentuk",
+ "style-panel.mixed": "Campuran",
+ "style-panel.opacity": "Transparansi",
+ "style-panel.position": "Posisi",
+ "style-panel.size": "Ukuran",
+ "style-panel.spline": "Spline",
+ "style-panel.title": "Gaya",
+ "style-panel.vertical-align": "Sejajar vertikal",
+ "toast.close": "Tutup",
+ "toast.error.copy-fail.desc": "Gagal menyalin gambar",
+ "toast.error.copy-fail.title": "Salinan gagal",
+ "toast.error.export-fail.desc": "Gagal mengekspor gambar",
+ "toast.error.export-fail.title": "Ekspor gagal",
+ "tool-panel.drawing": "Gambar",
+ "tool-panel.more": "Lanjut",
+ "tool-panel.shapes": "Bentuk",
+ "tool.arrow": "Panah",
+ "tool.arrow-down": "Panah bawah",
+ "tool.arrow-left": "Panah kiri",
+ "tool.arrow-right": "Panah kanan",
+ "tool.arrow-up": "Panah atas",
+ "tool.asset": "Aset",
+ "tool.check-box": "Kotak centang",
+ "tool.cloud": "Awan",
+ "tool.diamond": "Belah ketupat",
+ "tool.draw": "Menggambar",
+ "tool.ellipse": "Elips",
+ "tool.embed": "Sematkan",
+ "tool.eraser": "Penghapus",
+ "tool.frame": "Bingkai",
+ "tool.hand": "Tangan",
+ "tool.hexagon": "Segi enam",
+ "tool.highlight": "Sorot",
+ "tool.laser": "Laser",
+ "tool.line": "Garis",
+ "tool.note": "Catatan",
+ "tool.octagon": "Segi delapan",
+ "tool.oval": "Oval",
+ "tool.pentagon": "Segi lima",
+ "tool.rectangle": "Persegi panjang",
+ "tool.rhombus": "Jajaran genjang",
+ "tool.select": "Pilih",
+ "tool.star": "Bintang",
+ "tool.text": "Teks",
+ "tool.trapezoid": "Trapesium",
+ "tool.triangle": "Segitiga",
+ "tool.x-box": "Kotak X",
+ "verticalAlign-style.end": "Bawah",
+ "verticalAlign-style.middle": "Tengah",
+ "verticalAlign-style.start": "Atas",
+ "vscode.file-open.backup": "Cadangan",
+ "vscode.file-open.backup-failed": "Pencadangan gagal: ini bukan file .tldr.",
+ "vscode.file-open.backup-saved": "Cadangan disimpan",
+ "vscode.file-open.desc": "File ini dibuat dengan versi tldraw yang lebih lama. Apakah Anda ingin memperbaruinya agar dapat digunakan dengan versi yang baru?",
+ "vscode.file-open.dont-show-again": "Jangan tanya lagi",
+ "vscode.file-open.open": "Lanjutkan"
+}
diff --git a/apps/web/public/translations/it.json b/apps/web/public/translations/it.json
new file mode 100644
index 00000000..6c4d92dc
--- /dev/null
+++ b/apps/web/public/translations/it.json
@@ -0,0 +1,330 @@
+{
+ "action.align-bottom": "Allinea in basso",
+ "action.align-center-horizontal": "Allinea orizzontalmente",
+ "action.align-center-horizontal.short": "Allinea orizzontalmente",
+ "action.align-center-vertical": "Allinea verticalmente",
+ "action.align-center-vertical.short": "Allinea verticalmente",
+ "action.align-left": "Allinea a sinistra",
+ "action.align-right": "Allinea a destra",
+ "action.align-top": "Allinea in alto",
+ "action.back-to-content": "Torna al contenuto",
+ "action.bring-forward": "Porta avanti",
+ "action.bring-to-front": "Porta in primo piano",
+ "action.convert-to-bookmark": "Converti a segnalibro",
+ "action.convert-to-embed": "Converti in oggetto incorporato",
+ "action.copy": "Copia",
+ "action.copy-as-json": "Copia come JSON",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "Copia come PNG",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "Copia come SVG",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "Taglia",
+ "action.delete": "Elimina",
+ "action.distribute-horizontal": "Distribuire orizzontalmente",
+ "action.distribute-horizontal.short": "Distribuire orizzontalmente",
+ "action.distribute-vertical": "Distribuire verticalmente",
+ "action.distribute-vertical.short": "Distribuire verticalmente",
+ "action.duplicate": "Duplica",
+ "action.edit-link": "Modifica il collegamento",
+ "action.exit-pen-mode": "Esci dalla modalità penna",
+ "action.export-as-json": "Esporta come JSON",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "Esporta come PNG",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "Esporta come SVG",
+ "action.export-as-svg.short": "SVG",
+ "action.flip-horizontal": "Capovolgi orizzontalmente",
+ "action.flip-horizontal.short": "Capovolgi orizzontalmente",
+ "action.flip-vertical": "Capovolgi verticalmente",
+ "action.flip-vertical.short": "Capovolgi verticalmente",
+ "action.fork-project": "Crea una copia di questo progetto",
+ "action.group": "Raggruppa",
+ "action.insert-embed": "Inserisci oggetto incorporato",
+ "action.insert-media": "Carica contenuti multimediali",
+ "action.leave-shared-project": "Abbandona il progetto condiviso",
+ "action.new-project": "Nuovo progetto",
+ "action.new-shared-project": "Nuovo progetto condiviso",
+ "action.open-embed-link": "Apri il collegamento",
+ "action.open-file": "Apri file",
+ "action.pack": "Pacchetto",
+ "action.paste": "Incolla",
+ "action.print": "Stampa",
+ "action.redo": "Ripristina",
+ "action.rotate-ccw": "Ruota in senso antiorario",
+ "action.rotate-cw": "Ruota in senso orario",
+ "action.save-copy": "Salva una copia",
+ "action.select-all": "Seleziona tutto",
+ "action.select-none": "Deseleziona tutto",
+ "action.send-backward": "Porta indietro",
+ "action.send-to-back": "Porta in secondo piano",
+ "action.share-project": "Condividi questo progetto",
+ "action.stack-horizontal": "Impila orizzontalmente",
+ "action.stack-horizontal.short": "Impila orizzontalmente",
+ "action.stack-vertical": "Impila verticalmente",
+ "action.stack-vertical.short": "Impila verticalmente",
+ "action.stretch-horizontal": "Allunga orizzontalmente",
+ "action.stretch-horizontal.short": "Allunga orizzontalmente",
+ "action.stretch-vertical": "Allunga verticalmente",
+ "action.stretch-vertical.short": "Allunga verticalmente",
+ "action.toggle-auto-size": "Alterna le dimensioni automatiche",
+ "action.toggle-dark-mode": "Alterna tema scuro",
+ "action.toggle-dark-mode.menu": "Tema scuro",
+ "action.toggle-debug-mode": "Alterna modalità di debug",
+ "action.toggle-debug-mode.menu": "Modalità di debug",
+ "action.toggle-focus-mode": "Alterna modalità concentrazione",
+ "action.toggle-focus-mode.menu": "Modalità concentrazione",
+ "action.toggle-grid": "Alterna griglia",
+ "action.toggle-grid.menu": "Mostra griglia",
+ "action.toggle-reduce-motion": "Alterna riduzione del movimento",
+ "action.toggle-reduce-motion.menu": "Riduci il movimento",
+ "action.toggle-snap-mode": "Alterna allineamento automatico",
+ "action.toggle-snap-mode.menu": "Allinea sempre",
+ "action.toggle-tool-lock": "Alterna blocco strumenti",
+ "action.toggle-tool-lock.menu": "Blocco degli strumenti",
+ "action.toggle-transparent": "Alterna lo sfondo trasparente",
+ "action.toggle-transparent.context-menu": "Trasparente",
+ "action.toggle-transparent.menu": "Trasparente",
+ "action.undo": "Annulla",
+ "action.ungroup": "Separa",
+ "action.zoom-in": "Ingrandisci",
+ "action.zoom-out": "Rimpicciolisci",
+ "action.zoom-to-100": "Ingrandisci al 100%",
+ "action.zoom-to-fit": "Adatta allo schermo",
+ "action.zoom-to-selection": "Adatta alla selezione",
+ "actions-menu.title": "Azioni",
+ "align-style.end": "Fine",
+ "align-style.justify": "Giustifica",
+ "align-style.middle": "In mezzo",
+ "align-style.start": "Inizio",
+ "arrowheadEnd-style.arrow": "Freccia",
+ "arrowheadEnd-style.bar": "Barra",
+ "arrowheadEnd-style.diamond": "Diamante",
+ "arrowheadEnd-style.dot": "Punto",
+ "arrowheadEnd-style.inverted": "Invertito",
+ "arrowheadEnd-style.none": "Nessuna",
+ "arrowheadEnd-style.pipe": "Tubo",
+ "arrowheadEnd-style.square": "Quadrato",
+ "arrowheadEnd-style.triangle": "Triangolo",
+ "arrowheadStart-style.arrow": "Freccia",
+ "arrowheadStart-style.bar": "Barra",
+ "arrowheadStart-style.diamond": "Diamante",
+ "arrowheadStart-style.dot": "Punto",
+ "arrowheadStart-style.inverted": "Invertito",
+ "arrowheadStart-style.none": "Nessuna",
+ "arrowheadStart-style.pipe": "Tubo",
+ "arrowheadStart-style.square": "Quadrato",
+ "arrowheadStart-style.triangle": "Triangolo",
+ "color-style.black": "Nero",
+ "color-style.blue": "Blu",
+ "color-style.green": "Verde",
+ "color-style.grey": "Grigio",
+ "color-style.light-blue": "Azzurro",
+ "color-style.light-green": "Verde chiaro",
+ "color-style.light-red": "Rosso chiaro",
+ "color-style.light-violet": "Viola chiaro",
+ "color-style.orange": "Arancione",
+ "color-style.red": "Rosso",
+ "color-style.violet": "Viola",
+ "color-style.yellow": "Giallo",
+ "context-menu.arrange": "Disponi",
+ "context-menu.copy-as": "Copia come",
+ "context-menu.export-as": "Esporta come",
+ "context-menu.move-to-page": "Trasferisci su una pagina",
+ "context-menu.reorder": "Riordina",
+ "context.pages.new-page": "Nuova Pagina",
+ "dash-style.dashed": "Tratteggiato",
+ "dash-style.dotted": "Punteggiato",
+ "dash-style.draw": "Matita",
+ "dash-style.solid": "Solido",
+ "debug-panel.more": "Di più",
+ "edit-link-dialog.cancel": "Annulla",
+ "edit-link-dialog.clear": "Svuota",
+ "edit-link-dialog.detail": "I collegamenti verranno aperti in una nuova scheda",
+ "edit-link-dialog.invalid-url": "Un collegamento deve essere un URL valido",
+ "edit-link-dialog.save": "Continua",
+ "edit-link-dialog.title": "Modifica il collegamento",
+ "edit-link-dialog.url": "URL",
+ "edit-pages-dialog.move-down": "Sposta giù",
+ "edit-pages-dialog.move-up": "Sposta su",
+ "embed-dialog.back": "Indietro",
+ "embed-dialog.cancel": "Annulla",
+ "embed-dialog.create": "Crea",
+ "embed-dialog.instruction": "Incolla qui l'URL del sito per creare l'oggetto incorporato",
+ "embed-dialog.invalid-url": "Non è stato possibile creare un oggetto incorporato da quell'URL.",
+ "embed-dialog.title": "Crea oggetto incorporato",
+ "embed-dialog.url": "URL",
+ "file-system.confirm-clear.cancel": "Annulla",
+ "file-system.confirm-clear.continue": "Continua",
+ "file-system.confirm-clear.description": "La creazione di un nuovo progetto cancellerà il progetto corrente e tutte le modifiche non salvate andranno perse. Sei sicuro di voler continuare?",
+ "file-system.confirm-clear.dont-show-again": "Non chiedere di nuovo",
+ "file-system.confirm-clear.title": "Cancellare il progetto attuale?",
+ "file-system.confirm-open.cancel": "Annulla",
+ "file-system.confirm-open.description": "Aprire un file rimpiazzerà il tuo progetto attuale e ogni modifica non salvata verrà persa. Sei sicuro di voler continuare?",
+ "file-system.confirm-open.dont-show-again": "Non chiedere di nuovo",
+ "file-system.confirm-open.open": "Apri file",
+ "file-system.confirm-open.title": "Sovrascrivere il progetto corrente?",
+ "file-system.file-open-error.file-format-version-too-new": "Il file che hai provato ad aprire viene da una nuova versione di tldraw. Ricarica la pagina e riprova.",
+ "file-system.file-open-error.generic-corrupted-file": "Il file che hai provato ad aprire è corrotto",
+ "file-system.file-open-error.not-a-tldraw-file": "Il file che hai provato ad aprire non sembra essere un file tldraw.",
+ "file-system.file-open-error.title": "Impossibile aprile il file",
+ "file-system.shared-document-file-open-error.description": "L'apertura di file da progetti condivisi non è supportata",
+ "file-system.shared-document-file-open-error.title": "Impossibile aprire il file",
+ "fill-style.none": "Nessuno",
+ "fill-style.pattern": "Modello",
+ "fill-style.semi": "Semi",
+ "fill-style.solid": "Solido",
+ "focus-mode.toggle-focus-mode": "Alterna modalità concentrazione",
+ "font-style.draw": "Matita",
+ "font-style.mono": "Mono",
+ "font-style.sans": "Sans",
+ "font-style.serif": "Serif",
+ "geo-style.arrow-down": "Freccia in giù",
+ "geo-style.arrow-left": "Freccia a sinistra",
+ "geo-style.arrow-right": "Freccia a destra",
+ "geo-style.arrow-up": "Freccia in su",
+ "geo-style.check-box": "Casella di controllo",
+ "geo-style.diamond": "Diamante",
+ "geo-style.ellipse": "Ellisse",
+ "geo-style.hexagon": "Esagono",
+ "geo-style.octagon": "Ottagono",
+ "geo-style.oval": "Ovale",
+ "geo-style.pentagon": "Pentagono",
+ "geo-style.rectangle": "Rettangolo",
+ "geo-style.rhombus": "Rombo",
+ "geo-style.rhombus-2": "Rombo 2",
+ "geo-style.star": "Stella",
+ "geo-style.trapezoid": "Trapezio",
+ "geo-style.triangle": "Triangolo",
+ "geo-style.x-box": "Rettangolo con una X",
+ "help-menu.about": "A riguardo",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "Scorciatoie da tastiera",
+ "help-menu.title": "Aiuto e risorse",
+ "help-menu.twitter": "Twitter",
+ "menu.copy-as": "Copia come",
+ "menu.edit": "Modifica",
+ "menu.export-as": "Esporta come",
+ "menu.file": "File",
+ "menu.language": "Lingua",
+ "menu.preferences": "Preferenze",
+ "menu.title": "Menu",
+ "menu.view": "Visualizza",
+ "navigation-zone.toggle-minimap": "Alterna minimappa",
+ "navigation-zone.zoom": "Zoom",
+ "opacity-style.0.1": "10%",
+ "opacity-style.0.25": "25%",
+ "opacity-style.0.5": "50%",
+ "opacity-style.0.75": "75%",
+ "opacity-style.1": "100%",
+ "page-menu.create-new-page": "Crea nuova pagina",
+ "page-menu.edit-done": "Finito",
+ "page-menu.edit-start": "Modifica",
+ "page-menu.go-to-page": "Vai alla pagina",
+ "page-menu.max-page-count-reached": "Numero massimo di pagine raggiunte",
+ "page-menu.new-page-initial-name": "Pagina 1",
+ "page-menu.submenu.delete": "Elimina",
+ "page-menu.submenu.duplicate-page": "Duplica",
+ "page-menu.submenu.move-down": "Sposta giù",
+ "page-menu.submenu.move-up": "Sposta su",
+ "page-menu.submenu.rename": "Rinomina",
+ "page-menu.submenu.title": "Menu",
+ "page-menu.title": "Pagine",
+ "people-menu.change-color": "Cambia colore",
+ "people-menu.change-name": "Cambia nome",
+ "people-menu.invite": "Invita qualcun'altro",
+ "people-menu.title": "Persone",
+ "people-menu.user": "(Tu)",
+ "share-menu.copy-link": "Copia collegamento",
+ "share-menu.copy-link-note": "Chiunque abbia il collegamento potrà vedere e modificare questo progetto",
+ "share-menu.copy-readonly-link": "Copia il collegamento di sola lettura",
+ "share-menu.copy-readonly-link-note": "Chiunque abbia il collegamento potrà vedere (ma non modificare) questo progetto",
+ "share-menu.create-snapshot-link": "Crea un collegamento copia",
+ "share-menu.fork-note": "Crea un nuovo progetto condiviso basato su questa copia",
+ "share-menu.offline-note": "La condivisione di questo progetto creerà una copia in un nuovo URL. Potrai condividere l'URL con un massimo di 30 persone per visualizzare e modificare il progetto insieme",
+ "share-menu.project-too-large": "Spiacenti, questo progetto non può essere condiviso perché è troppo grande. Ci stiamo lavorando!",
+ "share-menu.readonly-link": "Sola lettura",
+ "share-menu.save-note": "Scarica questo progetto sul tuo computer come file .tldr",
+ "share-menu.share-project": "Condividi questo progetto",
+ "share-menu.snapshot-link-note": "Salva e condividi una copia statica del progetto attuale come un collegamento di sola lettura.",
+ "share-menu.title": "Condividi",
+ "share-menu.upload-failed": "Siamo spiacenti, al momento non è stato possibile caricare il progetto. Riprova o facci sapere se il problema persiste.",
+ "sharing.confirm-leave.cancel": "Annulla",
+ "sharing.confirm-leave.description": "Sei sicuro di voler abbandonare questo progetto condiviso? Puoi tornare navigando al suo URL.",
+ "sharing.confirm-leave.dont-show-again": "Non chiedere di nuovo",
+ "sharing.confirm-leave.title": "Abbandonare il progetto attuale?",
+ "shortcuts-dialog.edit": "Modifica",
+ "shortcuts-dialog.file": "File",
+ "shortcuts-dialog.preferences": "Preferenze",
+ "shortcuts-dialog.title": "Scorciatoie da tastiera",
+ "shortcuts-dialog.tools": "Strumenti",
+ "shortcuts-dialog.transform": "Trasforma",
+ "shortcuts-dialog.view": "Visualizzazione",
+ "size-style.l": "Grande",
+ "size-style.m": "Medio",
+ "size-style.s": "Piccolo",
+ "size-style.xl": "Molto grande",
+ "spline-style.cubic": "Cubico",
+ "spline-style.line": "Linea",
+ "style-panel.align": "Allineamento",
+ "style-panel.arrowhead-end": "Fine",
+ "style-panel.arrowhead-start": "Inizio",
+ "style-panel.arrowheads": "Punte di freccia",
+ "style-panel.color": "Colore",
+ "style-panel.dash": "Tratteggio",
+ "style-panel.fill": "Riempi",
+ "style-panel.font": "Font",
+ "style-panel.geo": "Forma",
+ "style-panel.mixed": "Misto",
+ "style-panel.opacity": "Opacità",
+ "style-panel.position": "Posizione",
+ "style-panel.size": "Dimensione",
+ "style-panel.spline": "Spline",
+ "style-panel.title": "Stile",
+ "style-panel.vertical-align": "Allineamento verticale",
+ "toast.close": "Chiudi",
+ "toast.error.copy-fail.desc": "Copia dell'immagine fallita",
+ "toast.error.copy-fail.title": "Copia fallita",
+ "toast.error.export-fail.desc": "Esportazione dell'immagine fallita",
+ "toast.error.export-fail.title": "Esportazione fallita",
+ "tool-panel.drawing": "Disegno",
+ "tool-panel.more": "Di più",
+ "tool-panel.shapes": "Forme",
+ "tool.arrow": "Freccia",
+ "tool.arrow-down": "Freccia in giù",
+ "tool.arrow-left": "Freccia a sinistra",
+ "tool.arrow-right": "Freccia a destra",
+ "tool.arrow-up": "Freccia in su",
+ "tool.asset": "Risorsa",
+ "tool.check-box": "Casella di controllo",
+ "tool.diamond": "Diamante",
+ "tool.draw": "Matita",
+ "tool.ellipse": "Ellisse",
+ "tool.embed": "Oggetto incorporato",
+ "tool.eraser": "Gomma",
+ "tool.frame": "Cornice",
+ "tool.hand": "Panoramica",
+ "tool.hexagon": "Esagono",
+ "tool.highlight": "Evidenziatore",
+ "tool.laser": "Laser",
+ "tool.line": "Linea",
+ "tool.note": "Post-it",
+ "tool.octagon": "Ottagono",
+ "tool.oval": "Ovale",
+ "tool.pentagon": "Pentagono",
+ "tool.rectangle": "Rettangolo",
+ "tool.rhombus": "Rombo",
+ "tool.select": "Seleziona",
+ "tool.star": "Stella",
+ "tool.text": "Casella di testo",
+ "tool.trapezoid": "Trapezio",
+ "tool.triangle": "Triangolo",
+ "tool.x-box": "Rettangolo con una X",
+ "vscode.file-open.backup": "Backup",
+ "vscode.file-open.backup-failed": "Backup non riuscito: questo non è un file .tldr",
+ "vscode.file-open.backup-saved": "Backup salvato",
+ "vscode.file-open.desc": "Questo file è stato creato con una versione precedente di tldraw. Desideri aggiornarlo per farlo funzionare con la nuova versione?",
+ "vscode.file-open.dont-show-again": "Non chiedere di nuovo",
+ "vscode.file-open.open": "Continua"
+}
diff --git a/apps/web/public/translations/ja.json b/apps/web/public/translations/ja.json
new file mode 100644
index 00000000..2967493f
--- /dev/null
+++ b/apps/web/public/translations/ja.json
@@ -0,0 +1,351 @@
+{
+ "action.align-bottom": "下寄せ",
+ "action.align-center-horizontal": "水平",
+ "action.align-center-horizontal.short": "水平",
+ "action.align-center-vertical": "垂直",
+ "action.align-center-vertical.short": "垂直",
+ "action.align-left": "左寄せ",
+ "action.align-right": "右寄せ",
+ "action.align-top": "上寄せ",
+ "action.back-to-content": "コンテンツに戻る",
+ "action.bring-forward": "ひとつ前に移動",
+ "action.bring-to-front": "最前面に移動",
+ "action.convert-to-bookmark": "ブックマークに変換",
+ "action.convert-to-embed": "埋め込みに変換",
+ "action.copy": "コピー",
+ "action.copy-as-json": "JSONとしてコピー",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "PNGとしてコピー",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "SVGとしてコピー",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "切り取り",
+ "action.delete": "削除",
+ "action.distribute-horizontal": "水平に並べる",
+ "action.distribute-horizontal.short": "水平に並べる",
+ "action.distribute-vertical": "垂直に並べる",
+ "action.distribute-vertical.short": "垂直に並べる",
+ "action.duplicate": "複製",
+ "action.edit-link": "リンク編集",
+ "action.exit-pen-mode": "ペンモードを終了する",
+ "action.export-as-json": "JSONとしてエクスポート",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "PNGとしてエクスポート",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "SVGとしてエクスポート",
+ "action.export-as-svg.short": "SVG",
+ "action.flip-horizontal": "水平方向に反転",
+ "action.flip-horizontal.short": "水平方向に反転",
+ "action.flip-vertical": "垂直方向に反転",
+ "action.flip-vertical.short": "垂直方向に反転",
+ "action.fork-project": "このプロジェクトをフォーク",
+ "action.group": "グルーピング",
+ "action.insert-embed": "埋め込みを挿入",
+ "action.insert-media": "メディアをアップロード",
+ "action.leave-shared-project": "共有プロジェクトを抜ける",
+ "action.new-project": "新規プロジェクト",
+ "action.new-shared-project": "新規共有プロジェクト",
+ "action.open-cursor-chat": "カーソルチャット",
+ "action.open-embed-link": "リンクを開く",
+ "action.open-file": "ファイルを開く",
+ "action.pack": "パック",
+ "action.paste": "貼り付け",
+ "action.print": "印刷",
+ "action.redo": "やり直し",
+ "action.rotate-ccw": "反時計回りに回転",
+ "action.rotate-cw": "時計回りに回転 ",
+ "action.save-copy": "コピーを保存",
+ "action.select-all": "すべて選択",
+ "action.select-none": "選択を解除",
+ "action.send-backward": "ひとつ後ろに移動",
+ "action.send-to-back": "最背面に移動",
+ "action.share-project": "このプロジェクトを共有",
+ "action.stack-horizontal": "水平にスタック",
+ "action.stack-horizontal.short": "水平にスタック",
+ "action.stack-vertical": "垂直にスタック",
+ "action.stack-vertical.short": "垂直にスタック",
+ "action.stop-following": "追跡を停止する",
+ "action.stretch-horizontal": "水平に引き伸ばす",
+ "action.stretch-horizontal.short": "水平に引き伸ばす",
+ "action.stretch-vertical": "垂直に引き伸ばす",
+ "action.stretch-vertical.short": "垂直に引き伸ばす",
+ "action.toggle-auto-size": "自動サイズ切り替え",
+ "action.toggle-dark-mode": "ダークモード",
+ "action.toggle-dark-mode.menu": "ダークモード",
+ "action.toggle-debug-mode": "デバッグモード",
+ "action.toggle-debug-mode.menu": "デバッグモード",
+ "action.toggle-focus-mode": "フォーカスモード",
+ "action.toggle-focus-mode.menu": "フォーカスモード",
+ "action.toggle-grid": "グリッドを表示",
+ "action.toggle-grid.menu": "グリッドを表示",
+ "action.toggle-lock": "ロック切り替え",
+ "action.toggle-reduce-motion": "モーションを減らす切り替え",
+ "action.toggle-reduce-motion.menu": "モーションを減らす",
+ "action.toggle-snap-mode": "スナップを常に表示",
+ "action.toggle-snap-mode.menu": "スナップを常に表示",
+ "action.toggle-tool-lock": "ツールロック切り替え",
+ "action.toggle-tool-lock.menu": "ツールロック",
+ "action.toggle-transparent": "透過を切り替え",
+ "action.toggle-transparent.context-menu": "透過",
+ "action.toggle-transparent.menu": "透過",
+ "action.undo": "元に戻す",
+ "action.ungroup": "グループ解除",
+ "action.unlock-all": "すべて解除",
+ "action.zoom-in": "拡大",
+ "action.zoom-out": "縮小",
+ "action.zoom-to-100": "100%へ拡大",
+ "action.zoom-to-fit": "拡大してすべてを表示",
+ "action.zoom-to-selection": "選択したアイテムに合わせて拡大",
+ "actions-menu.title": "アクション",
+ "align-style.end": "終点",
+ "align-style.justify": "均等",
+ "align-style.middle": "中央",
+ "align-style.start": "始点",
+ "arrowheadEnd-style.arrow": "矢印",
+ "arrowheadEnd-style.bar": "バー",
+ "arrowheadEnd-style.diamond": "菱形",
+ "arrowheadEnd-style.dot": "ドット",
+ "arrowheadEnd-style.inverted": "逆",
+ "arrowheadEnd-style.none": "なし",
+ "arrowheadEnd-style.pipe": "パイプ",
+ "arrowheadEnd-style.square": "四角形",
+ "arrowheadEnd-style.triangle": "三角形",
+ "arrowheadStart-style.arrow": "矢印",
+ "arrowheadStart-style.bar": "バー",
+ "arrowheadStart-style.diamond": "菱形",
+ "arrowheadStart-style.dot": "ドット",
+ "arrowheadStart-style.inverted": "逆",
+ "arrowheadStart-style.none": "なし",
+ "arrowheadStart-style.pipe": "パイプ",
+ "arrowheadStart-style.square": "四角形",
+ "arrowheadStart-style.triangle": "三角形",
+ "color-style.black": "黒色",
+ "color-style.blue": "青色",
+ "color-style.green": "緑色",
+ "color-style.grey": "灰色",
+ "color-style.light-blue": "水色",
+ "color-style.light-green": "黄緑色",
+ "color-style.light-red": "ピンク色",
+ "color-style.light-violet": "うす紫色",
+ "color-style.orange": "オレンジ色",
+ "color-style.red": "赤色",
+ "color-style.violet": "紫色",
+ "color-style.yellow": "黄色",
+ "context-menu.arrange": "並び替え",
+ "context-menu.copy-as": "形式を選択してコピー",
+ "context-menu.export-as": "形式を選択してエクスポート",
+ "context-menu.move-to-page": "ページへ移動",
+ "context-menu.reorder": "再整列",
+ "context.pages.new-page": "新規ページ",
+ "cursor-chat.type-to-chat": "チャットを開始...",
+ "dash-style.dashed": "ダッシュ",
+ "dash-style.dotted": "ドット",
+ "dash-style.draw": "描画",
+ "dash-style.solid": "実線",
+ "debug-panel.more": "もっと",
+ "edit-link-dialog.cancel": "キャンセル",
+ "edit-link-dialog.clear": "クリア",
+ "edit-link-dialog.detail": "リンクを新しいタブで開く",
+ "edit-link-dialog.invalid-url": "リンクは有効なURLである必要があります。",
+ "edit-link-dialog.save": "続ける",
+ "edit-link-dialog.title": "リンク編集",
+ "edit-link-dialog.url": "URL",
+ "edit-pages-dialog.move-down": "下へ移動",
+ "edit-pages-dialog.move-up": "上へ移動",
+ "embed-dialog.back": "戻る",
+ "embed-dialog.cancel": "キャンセル",
+ "embed-dialog.create": "作成",
+ "embed-dialog.instruction": "URLから埋め込みを作成",
+ "embed-dialog.invalid-url": "このURLから埋め込みを作成できませんでした",
+ "embed-dialog.title": "埋め込みを挿入",
+ "embed-dialog.url": "URL",
+ "file-system.confirm-clear.cancel": "キャンセル",
+ "file-system.confirm-clear.continue": "続ける",
+ "file-system.confirm-clear.description": "新しいプロジェクトを作成すると、現在のプロジェクトは終了し、保存されていない変更は破棄されます。よろしいですか?",
+ "file-system.confirm-clear.dont-show-again": "二度と表示しない",
+ "file-system.confirm-clear.title": "このプロジェクトをクリアしますか?",
+ "file-system.confirm-open.cancel": "キャンセル",
+ "file-system.confirm-open.description": "ファイルを開くと、現在のプロジェクトは終了し、保存されていない変更は破棄されます。よろしいですか?",
+ "file-system.confirm-open.dont-show-again": "二度と表示しない",
+ "file-system.confirm-open.open": "ファイルを開く",
+ "file-system.confirm-open.title": "現在のプロジェクトを上書きしますか?",
+ "file-system.file-open-error.file-format-version-too-new": "開こうとしているファイルは新しいバージョンの tldraw で作成されたものです。ページをリロードし、再度試してください",
+ "file-system.file-open-error.generic-corrupted-file": "開こうとしたファイルは壊れています",
+ "file-system.file-open-error.not-a-tldraw-file": "開こうとしているファイルは tldraw のファイルではありません",
+ "file-system.file-open-error.title": "ファイルを開くことができません",
+ "file-system.shared-document-file-open-error.description": "共有プロジェクトからファイルを開く操作は受け付けていません",
+ "file-system.shared-document-file-open-error.title": "ファイルを開くことができません",
+ "fill-style.none": "なし",
+ "fill-style.pattern": "パターン",
+ "fill-style.semi": "セミ",
+ "fill-style.solid": "ソリッド",
+ "focus-mode.toggle-focus-mode": "フォーカスモード",
+ "font-style.draw": "描画",
+ "font-style.mono": "Mono",
+ "font-style.sans": "Sans",
+ "font-style.serif": "Serif",
+ "geo-style.arrow-down": "下矢印",
+ "geo-style.arrow-left": "左矢印",
+ "geo-style.arrow-right": "右矢印",
+ "geo-style.arrow-up": "上矢印",
+ "geo-style.check-box": "チェックボックス",
+ "geo-style.cloud": "くも",
+ "geo-style.diamond": "菱形",
+ "geo-style.ellipse": "楕円形",
+ "geo-style.hexagon": "六角形",
+ "geo-style.octagon": "八角形",
+ "geo-style.oval": "楕円",
+ "geo-style.pentagon": "五角形",
+ "geo-style.rectangle": "長方形",
+ "geo-style.rhombus": "平行四辺形",
+ "geo-style.rhombus-2": "平行四辺形-2",
+ "geo-style.star": "星",
+ "geo-style.trapezoid": "台形",
+ "geo-style.triangle": "三角形",
+ "geo-style.x-box": "Xボックス",
+ "help-menu.about": "tldrawについて",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "キーボードショートカット",
+ "help-menu.title": "ヘルプ",
+ "help-menu.twitter": "X(Twitter)",
+ "home-project-dialog.description": "ここはあなたのためだけのローカルのホームプロジェクトです",
+ "home-project-dialog.ok": "Ok",
+ "home-project-dialog.title": "ホームプロジェクト",
+ "menu.copy-as": "形式を選択してコピー",
+ "menu.edit": "編集",
+ "menu.export-as": "形式を選択してエクスポート",
+ "menu.file": "ファイル",
+ "menu.language": "言語",
+ "menu.preferences": "設定",
+ "menu.title": "メニュー",
+ "menu.view": "表示",
+ "navigation-zone.toggle-minimap": "ミニマップを切り替え",
+ "navigation-zone.zoom": "ズーム",
+ "opacity-style.0.1": "10%",
+ "opacity-style.0.25": "25%",
+ "opacity-style.0.5": "50%",
+ "opacity-style.0.75": "75%",
+ "opacity-style.1": "100%",
+ "page-menu.create-new-page": "ページを作成",
+ "page-menu.edit-done": "完了",
+ "page-menu.edit-start": "編集",
+ "page-menu.go-to-page": "ページへ進む",
+ "page-menu.max-page-count-reached": "最大ページ数に到達しました",
+ "page-menu.new-page-initial-name": "ページ 1",
+ "page-menu.submenu.delete": "削除",
+ "page-menu.submenu.duplicate-page": "複製",
+ "page-menu.submenu.move-down": "下へ",
+ "page-menu.submenu.move-up": "上へ",
+ "page-menu.submenu.rename": "名前を変更",
+ "page-menu.submenu.title": "メニュー",
+ "page-menu.title": "ページ",
+ "people-menu.change-color": "色の変更",
+ "people-menu.change-name": "名前の変更",
+ "people-menu.follow": "追跡",
+ "people-menu.following": "追跡中",
+ "people-menu.invite": "招待する",
+ "people-menu.leading": "あなたを追跡中",
+ "people-menu.title": "参加者",
+ "people-menu.user": "(あなた)",
+ "rename-project-dialog.cancel": "キャンセル",
+ "rename-project-dialog.rename": "名前を変更",
+ "rename-project-dialog.title": "プロジェクトの名前を変更",
+ "share-menu.copy-link": "共有リンクをクリップボードにコピー",
+ "share-menu.copy-link-note": "リンクを知っている人は誰でもこのプロジェクトを閲覧、編集できます",
+ "share-menu.copy-readonly-link": "読み取り専用のリンクをコピー",
+ "share-menu.copy-readonly-link-note": "リンクを知っている人は誰でもこのプロジェクトを閲覧できます(編集はできません)",
+ "share-menu.create-snapshot-link": "スナップショットのリンクをコピー",
+ "share-menu.default-project-name": "共有プロジェクト",
+ "share-menu.fork-note": "このスナップショットから新しい共有プロジェクトを作成",
+ "share-menu.offline-note": "このプロジェクトから新しい共有プロジェクトを作成",
+ "share-menu.project-too-large": "このプロジェクトは大きすぎるため共有することができません。",
+ "share-menu.readonly-link": "読み取り専用",
+ "share-menu.save-note": "このプロジェクトを .tldr ファイルでダウンロード",
+ "share-menu.share-project": "このプロジェクトを共有",
+ "share-menu.snapshot-link-note": "このプロジェクトから読み取り専用のスナップショットリンク共有する",
+ "share-menu.title": "共有",
+ "share-menu.upload-failed": "一時的にあなたのプロジェクトをアップロードすることができません。もう一度試してください。",
+ "sharing.confirm-leave.cancel": "キャンセル",
+ "sharing.confirm-leave.description": "この共有プロジェクトから退出します。よろしいですか?このURLからいつでも復帰することができます。",
+ "sharing.confirm-leave.dont-show-again": "二度と表示しない",
+ "sharing.confirm-leave.leave": "退出する",
+ "sharing.confirm-leave.title": "現在のプロジェクトから退出する",
+ "shortcuts-dialog.collaboration": "コラボレーション",
+ "shortcuts-dialog.edit": "編集",
+ "shortcuts-dialog.file": "ファイル",
+ "shortcuts-dialog.preferences": "設定",
+ "shortcuts-dialog.title": "キーボードショートカット",
+ "shortcuts-dialog.tools": "ツール",
+ "shortcuts-dialog.transform": "変換",
+ "shortcuts-dialog.view": "表示",
+ "size-style.l": "大",
+ "size-style.m": "中",
+ "size-style.s": "小",
+ "size-style.xl": "特大",
+ "spline-style.cubic": "立方体",
+ "spline-style.line": "線",
+ "status.offline": "オフライン",
+ "status.online": "オンライン",
+ "style-panel.align": "配置",
+ "style-panel.arrowhead-end": "終点",
+ "style-panel.arrowhead-start": "始点",
+ "style-panel.arrowheads": "矢印",
+ "style-panel.color": "色",
+ "style-panel.dash": "線",
+ "style-panel.fill": "塗りつぶし",
+ "style-panel.font": "フォント",
+ "style-panel.geo": "形状",
+ "style-panel.mixed": "ミックス",
+ "style-panel.opacity": "透過度",
+ "style-panel.position": "ポジション",
+ "style-panel.size": "大きさ",
+ "style-panel.spline": "スプライン",
+ "style-panel.title": "スタイル",
+ "style-panel.vertical-align": "垂直に整列",
+ "toast.close": "閉じる",
+ "toast.error.copy-fail.desc": "画像のコピーに失敗しました",
+ "toast.error.copy-fail.title": "コピーに失敗しました",
+ "toast.error.export-fail.desc": "画像のエクスポートに失敗しました",
+ "toast.error.export-fail.title": "エクスポートに失敗しました",
+ "tool-panel.drawing": "描画",
+ "tool-panel.more": "もっと",
+ "tool-panel.shapes": "形状",
+ "tool.arrow": "矢印",
+ "tool.arrow-down": "下矢印",
+ "tool.arrow-left": "左矢印",
+ "tool.arrow-right": "右矢印",
+ "tool.arrow-up": "上矢印",
+ "tool.asset": "アセット",
+ "tool.check-box": "チェックボックス",
+ "tool.cloud": "くも",
+ "tool.diamond": "菱形",
+ "tool.draw": "描画",
+ "tool.ellipse": "楕円形",
+ "tool.embed": "埋め込み",
+ "tool.eraser": "消しゴム",
+ "tool.frame": "フレーム",
+ "tool.hand": "手",
+ "tool.hexagon": "六角形",
+ "tool.highlight": "ハイライト",
+ "tool.laser": "レーザー",
+ "tool.line": "線",
+ "tool.note": "ふせん",
+ "tool.octagon": "八角形",
+ "tool.oval": "楕円",
+ "tool.pentagon": "五角形",
+ "tool.rectangle": "長方形",
+ "tool.rhombus": "平行四辺形",
+ "tool.select": "選択",
+ "tool.star": "星",
+ "tool.text": "テキスト",
+ "tool.trapezoid": "台形",
+ "tool.triangle": "三角形",
+ "tool.x-box": "Xボックス",
+ "vscode.file-open.backup": "バックアップ",
+ "vscode.file-open.backup-failed": "バックアップ失敗: .tldr ファイルではありません",
+ "vscode.file-open.backup-saved": "バックアップを保存",
+ "vscode.file-open.desc": "このバージョンの tldraw のためにドキュメントを更新しました。オリジナルのバージョンを表示したい場合は以下からバックアップを作成してください",
+ "vscode.file-open.dont-show-again": "二度と表示しない",
+ "vscode.file-open.open": "続く"
+}
diff --git a/apps/web/public/translations/ko-kr.json b/apps/web/public/translations/ko-kr.json
new file mode 100644
index 00000000..786759ea
--- /dev/null
+++ b/apps/web/public/translations/ko-kr.json
@@ -0,0 +1,372 @@
+{
+ "action.align-bottom": "아래쪽 정렬",
+ "action.align-center-horizontal": "가로 정렬",
+ "action.align-center-horizontal.short": "가로 정렬",
+ "action.align-center-vertical": "세로 정렬",
+ "action.align-center-vertical.short": "세로 정렬",
+ "action.align-left": "왼쪽 정렬",
+ "action.align-right": "오른쪽 정렬",
+ "action.align-top": "위쪽 정렬",
+ "action.back-to-content": "콘텐츠로 돌아가기",
+ "action.bring-forward": "앞으로 가져오기",
+ "action.bring-to-front": "맨 앞으로 가져오기",
+ "action.convert-to-bookmark": "북마크로 변환",
+ "action.convert-to-embed": "임베드로 변환",
+ "action.copy": "복사",
+ "action.copy-as-json": "JSON으로 복사",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "PNG로 복사",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "SVG로 복사",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "자르기",
+ "action.delete": "삭제하기",
+ "action.distribute-horizontal": "가로로 균등 배치",
+ "action.distribute-horizontal.short": "가로로 균등 배치",
+ "action.distribute-vertical": "세로로 균등 배치",
+ "action.distribute-vertical.short": "세로로 균등 배치",
+ "action.duplicate": "복제하기",
+ "action.edit-link": "링크 수정",
+ "action.exit-pen-mode": "펜 모드 종료",
+ "action.export-all-as-json": "모두 JSON으로 내보내기",
+ "action.export-all-as-json.short": "JSON",
+ "action.export-all-as-png": "모두 PNG로 내보내기",
+ "action.export-all-as-png.short": "PNG",
+ "action.export-all-as-svg": "모두 SVG로 내보내기",
+ "action.export-all-as-svg.short": "SVG",
+ "action.export-as-json": "JSON으로 내보내기",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "PNG로 내보내기",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "SVG로 내보내기",
+ "action.export-as-svg.short": "SVG",
+ "action.fit-frame-to-content": "콘텐츠에 맞추기",
+ "action.flip-horizontal": "가로로 뒤집기",
+ "action.flip-horizontal.short": "가로로 뒤집기",
+ "action.flip-vertical": "세로로 뒤집기",
+ "action.flip-vertical.short": "세로로 뒤집기",
+ "action.fork-project": "이 프로젝트를 포크하세요",
+ "action.group": "그룹화",
+ "action.insert-embed": "임베드 삽입",
+ "action.insert-media": "미디어 업로드",
+ "action.leave-shared-project": "공유 프로젝트에서 나가기",
+ "action.new-project": "새 프로젝트",
+ "action.new-shared-project": "새 공유 프로젝트",
+ "action.open-cursor-chat": "커서 채팅",
+ "action.open-embed-link": "링크 열기",
+ "action.open-file": "파일 열기",
+ "action.pack": "그룹정렬",
+ "action.paste": "붙여넣기",
+ "action.print": "인쇄",
+ "action.redo": "다시 실행",
+ "action.remove-frame": "프레임 삭제",
+ "action.rename": "이름 바꾸기",
+ "action.rotate-ccw": "반시계 반향으로 회전",
+ "action.rotate-cw": "시계 방향으로 회전",
+ "action.save-copy": "사본 저장",
+ "action.select-all": "전체 선택",
+ "action.select-none": "선택 안함",
+ "action.send-backward": "뒤로 보내기",
+ "action.send-to-back": "맨 뒤로 보내기",
+ "action.share-project": "이 프로젝트 공유",
+ "action.stack-horizontal": "수평으로 쌓기",
+ "action.stack-horizontal.short": "수평으로 쌓기",
+ "action.stack-vertical": "수직으로 쌓기",
+ "action.stack-vertical.short": "수직으로 쌓기",
+ "action.stop-following": "팔로우 중지",
+ "action.stretch-horizontal": "수평으로 늘리기",
+ "action.stretch-horizontal.short": "수평으로 늘리기",
+ "action.stretch-vertical": "수직으로 늘리기",
+ "action.stretch-vertical.short": "수직으로 늘리기",
+ "action.toggle-auto-size": "자동 크기 전환",
+ "action.toggle-dark-mode": "다크모드 전환",
+ "action.toggle-dark-mode.menu": "다크 모드",
+ "action.toggle-debug-mode": "디버그 모드 전환",
+ "action.toggle-debug-mode.menu": "디버그 모드",
+ "action.toggle-edge-scrolling": "가장자리 스크롤 전환",
+ "action.toggle-edge-scrolling.menu": "가장자리 스크롤",
+ "action.toggle-focus-mode": "포커스 모드 전환",
+ "action.toggle-focus-mode.menu": "포커스 모드",
+ "action.toggle-grid": "그리드 전환",
+ "action.toggle-grid.menu": "그리드 보기",
+ "action.toggle-lock": "잠금/잠금 해제",
+ "action.toggle-reduce-motion": "움직임 줄이기 전환",
+ "action.toggle-reduce-motion.menu": "움직임 줄이기",
+ "action.toggle-snap-mode": "항상 스냅 전환",
+ "action.toggle-snap-mode.menu": "항상 스냅",
+ "action.toggle-tool-lock": "도구 잠금 전환",
+ "action.toggle-tool-lock.menu": "도구 잠금",
+ "action.toggle-transparent": "투명 배경 전환",
+ "action.toggle-transparent.context-menu": "투명하게 하기",
+ "action.toggle-transparent.menu": "투명하게 하기",
+ "action.toggle-wrap-mode": "줄 바꿈 시 선택 전환",
+ "action.toggle-wrap-mode.menu": "줄 바꿈 시 선택",
+ "action.undo": "실행 취소",
+ "action.ungroup": "그룹화 해제",
+ "action.unlock-all": "모두 잠금 해제",
+ "action.zoom-in": "확대",
+ "action.zoom-out": "축소",
+ "action.zoom-to-100": "100% 맞추기",
+ "action.zoom-to-fit": "전체에 맞추기",
+ "action.zoom-to-selection": "선택 요소에 맞추기",
+ "actions-menu.title": "액션",
+ "align-style.end": "오른쪽",
+ "align-style.justify": "양쪽정렬",
+ "align-style.middle": "가운데",
+ "align-style.start": "왼쪽",
+ "arrowheadEnd-style.arrow": "화살표",
+ "arrowheadEnd-style.bar": "막대",
+ "arrowheadEnd-style.diamond": "다이아몬드",
+ "arrowheadEnd-style.dot": "원형",
+ "arrowheadEnd-style.inverted": "역삼각형",
+ "arrowheadEnd-style.none": "없음",
+ "arrowheadEnd-style.pipe": "막대",
+ "arrowheadEnd-style.square": "사각형",
+ "arrowheadEnd-style.triangle": "삼각형",
+ "arrowheadStart-style.arrow": "화살표",
+ "arrowheadStart-style.bar": "막대",
+ "arrowheadStart-style.diamond": "다이아몬드",
+ "arrowheadStart-style.dot": "원형",
+ "arrowheadStart-style.inverted": "역삼각형",
+ "arrowheadStart-style.none": "없음",
+ "arrowheadStart-style.pipe": "막대",
+ "arrowheadStart-style.square": "사각형",
+ "arrowheadStart-style.triangle": "삼각형",
+ "assets.files.upload-failed": "업로드 실패",
+ "assets.url.failed": "URL 미리보기를 로드할 수 없습니다.",
+ "color-style.black": "검정",
+ "color-style.blue": "파랑",
+ "color-style.green": "초록",
+ "color-style.grey": "회색",
+ "color-style.light-blue": "하늘",
+ "color-style.light-green": "연두",
+ "color-style.light-red": "연홍",
+ "color-style.light-violet": "연보라",
+ "color-style.orange": "주황",
+ "color-style.red": "빨강",
+ "color-style.violet": "보라",
+ "color-style.white": "흰색",
+ "color-style.yellow": "노랑",
+ "context-menu.arrange": "정돈",
+ "context-menu.copy-as": "복사하기",
+ "context-menu.export-all-as": "다른 형식으로 내보내기",
+ "context-menu.export-as": "내보내기",
+ "context-menu.move-to-page": "페이지로 이동",
+ "context-menu.reorder": "재정렬",
+ "context.pages.new-page": "새 페이지",
+ "cursor-chat.type-to-chat": "채팅하려면 입력하세요...",
+ "dash-style.dashed": "파선",
+ "dash-style.dotted": "점선",
+ "dash-style.draw": "그린선",
+ "dash-style.solid": "선",
+ "debug-panel.more": "자세히",
+ "document.default-name": "제목 없음",
+ "edit-link-dialog.cancel": "취소",
+ "edit-link-dialog.clear": "지우기",
+ "edit-link-dialog.detail": "새탭으로 링크가 열립니다.",
+ "edit-link-dialog.invalid-url": "링크는 유효한 URL이어야 합니다.",
+ "edit-link-dialog.save": "계속하기",
+ "edit-link-dialog.title": "링크 수정",
+ "edit-link-dialog.url": "URL",
+ "edit-pages-dialog.move-down": "아래로 이동",
+ "edit-pages-dialog.move-up": "위로 이동",
+ "embed-dialog.back": "뒤로",
+ "embed-dialog.cancel": "취소",
+ "embed-dialog.create": "생성하기",
+ "embed-dialog.instruction": "임베드를 만들 사이트의 URL을 붙여넣습니다.",
+ "embed-dialog.invalid-url": "해당 URL에서 임베드를 만들 수 없습니다.",
+ "embed-dialog.title": "임베드 만들기",
+ "embed-dialog.url": "URL",
+ "file-system.confirm-clear.cancel": "취소",
+ "file-system.confirm-clear.continue": "계속",
+ "file-system.confirm-clear.description": "새 프로젝트를 만들면 현재 프로젝트가 지워지고 저장되지 않은 변경 사항은 모두 저장되지 않습니다. 계속 진행하시겠습니까?",
+ "file-system.confirm-clear.dont-show-again": "다시 묻지 마세요",
+ "file-system.confirm-clear.title": "현재 프로젝트를 지우시겠습니까?",
+ "file-system.confirm-open.cancel": "취소",
+ "file-system.confirm-open.description": "파일을 열면 현재 프로젝트가 대체되고 저장되지 않은 변경 내용은 손실됩니다. 계속하시겠습니까?",
+ "file-system.confirm-open.dont-show-again": "다시 묻지 않음",
+ "file-system.confirm-open.open": "파일 열기",
+ "file-system.confirm-open.title": "현재 프로젝트를 덮어쓰시겠습니까?",
+ "file-system.file-open-error.file-format-version-too-new": "해당 파일은 더 최신 버전의 tldraw 파일 입니다. 페이지를 새로고침하고 다시 시도하세요.",
+ "file-system.file-open-error.generic-corrupted-file": "해당 파일이 손상되었습니다.",
+ "file-system.file-open-error.not-a-tldraw-file": "해당 파일은 tldraw 파일이 아닙니다.",
+ "file-system.file-open-error.title": "파일을 열 수 없습니다.",
+ "file-system.shared-document-file-open-error.description": "공유 프로젝트에서 파일 열기는 지원되지 않습니다.",
+ "file-system.shared-document-file-open-error.title": "파일을 열 수 없습니다.",
+ "fill-style.none": "없음",
+ "fill-style.pattern": "패턴",
+ "fill-style.semi": "무색",
+ "fill-style.solid": "단색",
+ "focus-mode.toggle-focus-mode": "포커스 모드 전환",
+ "font-style.draw": "필기체",
+ "font-style.mono": "고정폭",
+ "font-style.sans": "돋움체",
+ "font-style.serif": "바탕체",
+ "geo-style.arrow-down": "아래쪽 화살표",
+ "geo-style.arrow-left": "왼쪽 화살표",
+ "geo-style.arrow-right": "오른쪽 화살표",
+ "geo-style.arrow-up": "위쪽 화살표",
+ "geo-style.check-box": "체크박스",
+ "geo-style.cloud": "클라우드",
+ "geo-style.diamond": "마름모꼴",
+ "geo-style.ellipse": "원형",
+ "geo-style.hexagon": "육각형",
+ "geo-style.octagon": "팔각형",
+ "geo-style.oval": "타원형",
+ "geo-style.pentagon": "오각형",
+ "geo-style.rectangle": "사각형",
+ "geo-style.rhombus": "평행사변형",
+ "geo-style.rhombus-2": "평행사변형",
+ "geo-style.star": "별",
+ "geo-style.trapezoid": "사다리꼴",
+ "geo-style.triangle": "삼각형",
+ "geo-style.x-box": "X박스",
+ "help-menu.about": "소개",
+ "help-menu.discord": "디스코드",
+ "help-menu.github": "깃허브",
+ "help-menu.keyboard-shortcuts": "키보드 단축키",
+ "help-menu.title": "도움말 및 자료",
+ "help-menu.twitter": "트위터",
+ "home-project-dialog.description": "이것은 나만의 로컬 홈 프로젝트입니다.",
+ "home-project-dialog.ok": "확인",
+ "home-project-dialog.title": "홈 프로젝트",
+ "menu.copy-as": "복사하기",
+ "menu.edit": "편집",
+ "menu.export-as": "내보내기",
+ "menu.file": "파일",
+ "menu.language": "언어",
+ "menu.preferences": "설정",
+ "menu.title": "메뉴",
+ "menu.view": "보기",
+ "navigation-zone.toggle-minimap": "미니맵 전환",
+ "navigation-zone.zoom": "확대/축소",
+ "opacity-style.0.1": "10%",
+ "opacity-style.0.25": "25%",
+ "opacity-style.0.5": "50%",
+ "opacity-style.0.75": "75%",
+ "opacity-style.1": "100%",
+ "page-menu.create-new-page": "새 페이지 만들기",
+ "page-menu.edit-done": "완료",
+ "page-menu.edit-start": "수정",
+ "page-menu.go-to-page": "페이지로 이동",
+ "page-menu.max-page-count-reached": "최대 페이지 도달",
+ "page-menu.new-page-initial-name": "페이지 1",
+ "page-menu.submenu.delete": "삭제하기",
+ "page-menu.submenu.duplicate-page": "복제하기",
+ "page-menu.submenu.move-down": "아래로 이동",
+ "page-menu.submenu.move-up": "위로 이동",
+ "page-menu.submenu.rename": "이름 바꾸기",
+ "page-menu.submenu.title": "메뉴",
+ "page-menu.title": "페이지",
+ "people-menu.change-color": "색상 변경",
+ "people-menu.change-name": "이름 변경",
+ "people-menu.follow": "팔로우",
+ "people-menu.following": "팔로잉",
+ "people-menu.invite": "다른 사용자 초대",
+ "people-menu.leading": "팔로우하기",
+ "people-menu.title": "사용자",
+ "people-menu.user": "(나)",
+ "rename-project-dialog.cancel": "취소",
+ "rename-project-dialog.rename": "이름 변경",
+ "rename-project-dialog.title": "프로젝트 이름 바꾸기",
+ "share-menu.copy-link": "링크 복사",
+ "share-menu.copy-link-note": "링크가 있는 사람은 누구나 이 프로젝트를 보고 편집할 수 있습니다.",
+ "share-menu.copy-readonly-link": "읽기 전용 링크 복사",
+ "share-menu.copy-readonly-link-note": "링크가 있는 사람은 누구나 이 프로젝트를 볼 수 있지만 편집할 수는 없습니다.",
+ "share-menu.create-snapshot-link": "스냅샷 링크 만들기",
+ "share-menu.default-project-name": "공유 프로젝트",
+ "share-menu.fork-note": "이 스냅샷을 기반으로 새 공유 프로젝트를 만듭니다.",
+ "share-menu.offline-note": "공유하기를 실행하면 공유 가능한 사본 URL이 생성됩니다. 해당 URL을 통해 최대 30명의 다른 사용자와 함께 프로젝트를 보고 편집할 수 있습니다.",
+ "share-menu.project-too-large": "너무 큰 프로젝트는 공유할 수 없습니다. 현재 개선중이니 참고해주세요!",
+ "share-menu.readonly-link": "읽기 전용",
+ "share-menu.save-note": "이 프로젝트를 컴퓨터에 .tldr 파일로 다운로드합니다.",
+ "share-menu.share-project": "이 프로젝트 공유하기",
+ "share-menu.snapshot-link-note": "이 프로젝트를 읽기 전용 스냅샷 링크로 캡처하고 공유하세요.",
+ "share-menu.title": "공유",
+ "share-menu.upload-failed": "죄송합니다. 지금은 프로젝트를 업로드할 수 없습니다. 다시 시도하거나 문제가 지속되면 알려주시기 바랍니다.",
+ "sharing.confirm-leave.cancel": "취소",
+ "sharing.confirm-leave.description": "이 공유 프로젝트에서 나가시겠습니까? 해당 URL로 이동하여 해당 페이지로 돌아갈 수 있습니다.",
+ "sharing.confirm-leave.dont-show-again": "다시 묻지 않음",
+ "sharing.confirm-leave.leave": "떠나기",
+ "sharing.confirm-leave.title": "현재 프로젝트에서 나가시겠습니까?",
+ "shortcuts-dialog.collaboration": "협업",
+ "shortcuts-dialog.edit": "편집",
+ "shortcuts-dialog.file": "파일",
+ "shortcuts-dialog.preferences": "설정",
+ "shortcuts-dialog.title": "키보드 단축키",
+ "shortcuts-dialog.tools": "도구",
+ "shortcuts-dialog.transform": "변환",
+ "shortcuts-dialog.view": "보기",
+ "size-style.l": "대",
+ "size-style.m": "중",
+ "size-style.s": "소",
+ "size-style.xl": "특대",
+ "spline-style.cubic": "큐빅",
+ "spline-style.line": "선",
+ "status.offline": "오프라인",
+ "status.online": "온라인",
+ "style-panel.align": "정렬",
+ "style-panel.arrowhead-end": "끝 모양",
+ "style-panel.arrowhead-start": "시작 모양",
+ "style-panel.arrowheads": "화살촉",
+ "style-panel.color": "색깔",
+ "style-panel.dash": "테두리",
+ "style-panel.fill": "채우기",
+ "style-panel.font": "글꼴",
+ "style-panel.geo": "모양",
+ "style-panel.mixed": "혼합된",
+ "style-panel.opacity": "불투명도",
+ "style-panel.position": "위치",
+ "style-panel.size": "크기",
+ "style-panel.spline": "스플라인 곡선",
+ "style-panel.title": "스타일",
+ "style-panel.vertical-align": "수직 정렬",
+ "toast.close": "닫기",
+ "toast.error.copy-fail.desc": "이미지를 복사하지 못했습니다.",
+ "toast.error.copy-fail.title": "복사 실패",
+ "toast.error.export-fail.desc": "이미지를 내보내지 못했습니다.",
+ "toast.error.export-fail.title": "내보내기 실패",
+ "tool-panel.drawing": "그리기",
+ "tool-panel.more": "자세히",
+ "tool-panel.shapes": "모양",
+ "tool.arrow": "화살표",
+ "tool.arrow-down": "아래쪽 화살표",
+ "tool.arrow-left": "왼쪽 화살표",
+ "tool.arrow-right": "오른쪽 화살표",
+ "tool.arrow-up": "위쪽 화살표",
+ "tool.asset": "미디어",
+ "tool.check-box": "체크박스",
+ "tool.cloud": "클라우드",
+ "tool.diamond": "마름모꼴",
+ "tool.draw": "그리기",
+ "tool.ellipse": "원형",
+ "tool.embed": "임베드",
+ "tool.eraser": "지우개",
+ "tool.frame": "프레임",
+ "tool.hand": "핸드툴",
+ "tool.hexagon": "육각형",
+ "tool.highlight": "하이라이트",
+ "tool.laser": "레이저",
+ "tool.line": "선",
+ "tool.note": "메모",
+ "tool.octagon": "팔각형",
+ "tool.oval": "타원",
+ "tool.pentagon": "오각형",
+ "tool.rectangle": "사각형",
+ "tool.rhombus": "마름모",
+ "tool.select": "선택",
+ "tool.star": "별",
+ "tool.text": "문자",
+ "tool.trapezoid": "사다리꼴",
+ "tool.triangle": "삼각형",
+ "tool.x-box": "X박스",
+ "verticalAlign-style.end": "아래",
+ "verticalAlign-style.middle": "중간",
+ "verticalAlign-style.start": "위",
+ "vscode.file-open.backup": "백업",
+ "vscode.file-open.backup-failed": "백업에 실패했습니다: .tldr 파일이 아닙니다.",
+ "vscode.file-open.backup-saved": "백업 저장완료",
+ "vscode.file-open.desc": "이 파일은 이전 버전의 tldraw로 생성되었습니다. 새 버전에서 작동하도록 업데이트하시겠습니까?",
+ "vscode.file-open.dont-show-again": "다시 묻지 않음",
+ "vscode.file-open.open": "계속하기"
+}
diff --git a/apps/web/public/translations/ku.json b/apps/web/public/translations/ku.json
new file mode 100644
index 00000000..41fb8a59
--- /dev/null
+++ b/apps/web/public/translations/ku.json
@@ -0,0 +1,93 @@
+{
+ "action.bring-forward": "بڕۆ پێشەوە",
+ "action.bring-to-front": "بگوازرێتەوە بۆ پێشەوە",
+ "action.copy": "کۆپی بکە",
+ "action.cut": "بڕین",
+ "action.delete": "سڕینەوە",
+ "action.duplicate": "دووبارەکردنەوە",
+ "action.flip-horizontal": "ئاسۆیی وەرگەڕاندن",
+ "action.flip-vertical": "ستونی وەرگەڕاندن",
+ "action.flip-horizontal.short": "ئاسۆیی وەرگەڕاندن",
+ "action.flip-vertical.short": "ستونی وەرگەڕاندن",
+ "action.group": "کۆمەڵە",
+ "action.insert-media": "داگرتنی میدیا",
+ "action.paste": "پەیست بکە",
+ "action.redo": "دووبارە بیکەرەوە",
+ "action.select-all": "هەموویان هەڵبژێره",
+ "action.select-none": "هیچ هه‌ڵمه‌بژێره‌",
+ "action.send-backward": "بەرەو دواوە",
+ "action.send-to-back": "بچۆ بۆ پشتەوە",
+ "action.toggle-dark-mode.menu": "دۆخی تاریک",
+ "action.toggle-dark-mode": "دۆخی تاریک",
+ "action.toggle-debug-mode.menu": "مۆدی هەڵەدۆزین",
+ "action.toggle-debug-mode": "مۆدی هەڵەدۆزین",
+ "action.toggle-focus-mode.menu": "دۆخی فۆکەس",
+ "action.toggle-focus-mode": "دۆخی فۆکەس",
+ "action.toggle-grid.menu": "تۆڕی پیشان بدە",
+ "action.toggle-grid": "تۆڕی پیشان بدە",
+ "action.toggle-snap-mode.menu": "هەمیشە وێنەی خێرا پیشان بدە",
+ "action.toggle-snap-mode": "هەمیشە وێنەی خێرا پیشان بدە",
+ "action.toggle-transparent.context-menu": "ڕوون",
+ "action.toggle-transparent.menu": "ڕوون",
+ "action.undo": "پاشەکشە بکە",
+ "action.ungroup": "لابردنی کۆمەڵە",
+ "action.zoom-in": "هێنانە پێشەوە",
+ "action.zoom-out": "دوور خستنەوە",
+ "action.zoom-to-fit": "زووم بکە بۆ ئەوەی لەگەڵیدا بگونجێت",
+ "action.zoom-to-selection": "زووم بکە بۆ هەڵبژاردن",
+ "dash-style.draw": "وێنەکێشان",
+ "font-style.draw": "وێنەکێشان",
+ "geo-style.ellipse": "بیبلی",
+ "geo-style.rectangle": "لاکێشە",
+ "geo-style.triangle": "سێگۆشە",
+ "arrowheadStart-style.arrow": "تیر",
+ "arrowheadStart-style.triangle": "سێگۆشە",
+ "arrowheadEnd-style.arrow": "تیر",
+ "arrowheadEnd-style.triangle": "سێگۆشە",
+ "spline-style.line": "هێڵ",
+ "tool.select": "دەسنیاشنکردن",
+ "tool.draw": "وێنەکێشان",
+ "tool.eraser": "سڕەرەوە",
+ "tool.arrow": "تیر",
+ "tool.ellipse": "بیبلی",
+ "tool.line": "هێڵ",
+ "tool.rectangle": "لاکێشە",
+ "tool.triangle": "سێگۆشە",
+ "tool.note": "چەسپاو",
+ "tool.text": "دەق",
+ "menu.copy-as": "کۆپی وەک",
+ "menu.edit": "دەستکاری",
+ "menu.export-as": "هەناردەکردن وەک",
+ "menu.file": "فایلێک",
+ "menu.language": "زمان",
+ "menu.preferences": "خواست",
+ "menu.view": "دیمەن",
+ "context-menu.copy-as": "کۆپی وەک",
+ "context-menu.export-as": "هەناردەکردن وەک",
+ "context-menu.move-to-page": "بچۆ بۆ لاپەڕە",
+ "page-menu.create-new-page": "دروستکردنی لاپەڕە",
+ "page-menu.edit-start": "دەستکاری",
+ "page-menu.submenu.duplicate-page": "دووبارەکردنەوە",
+ "page-menu.submenu.delete": "سڕینەوە",
+ "share-menu.copy-link": "لینکی بانگهێشتکردن کۆپی بکە",
+ "share-menu.copy-readonly-link": "بە شێوەیەکی هەڕەمەکی کۆپی بکە",
+ "help-menu.keyboard-shortcuts": "کورتکراوەکانی تەختەکلیل",
+ "edit-link-dialog.cancel": "ڕەتکردنەوە",
+ "embed-dialog.cancel": "ڕەتکردنەوە",
+ "shortcuts-dialog.title": "کورتکراوەکانی تەختەکلیل",
+ "shortcuts-dialog.edit": "دەستکاری",
+ "shortcuts-dialog.file": "فایلێک",
+ "shortcuts-dialog.preferences": "خواست",
+ "shortcuts-dialog.tools": "ئامرازەکان",
+ "shortcuts-dialog.transform": "گۆڕین",
+ "shortcuts-dialog.view": "دیمەن",
+ "style-panel.title": "نەخشەکان",
+ "style-panel.align": "ڕێکخستن",
+ "style-panel.color": "رەنگ",
+ "style-panel.dash": "لەت لەت",
+ "style-panel.fill": "پڕکردنەوە",
+ "style-panel.font": "هێڵ",
+ "style-panel.size": "قەبارە",
+ "focus-mode.toggle-focus-mode": "دۆخی فۆکەس",
+ "file-system.confirm-open.cancel": "ڕەتکردنەوە"
+}
diff --git a/apps/web/public/translations/languages.json b/apps/web/public/translations/languages.json
new file mode 100644
index 00000000..b05d868f
--- /dev/null
+++ b/apps/web/public/translations/languages.json
@@ -0,0 +1,150 @@
+[
+ {
+ "locale": "ar",
+ "label": "عربي"
+ },
+ {
+ "locale": "ca",
+ "label": "Català"
+ },
+ {
+ "locale": "cs",
+ "label": "Čeština"
+ },
+ {
+ "locale": "da",
+ "label": "Danish"
+ },
+ {
+ "locale": "de",
+ "label": "Deutsch"
+ },
+ {
+ "locale": "en",
+ "label": "English"
+ },
+ {
+ "locale": "es",
+ "label": "Español"
+ },
+ {
+ "locale": "fa",
+ "label": "فارسی"
+ },
+ {
+ "locale": "fi",
+ "label": "Suomi"
+ },
+ {
+ "locale": "fr",
+ "label": "Français"
+ },
+ {
+ "locale": "gl",
+ "label": "Galego"
+ },
+ {
+ "locale": "he",
+ "label": "עברית"
+ },
+ {
+ "locale": "hr",
+ "label": "Hrvatski"
+ },
+ {
+ "locale": "id",
+ "label": "Bahasa Indonesia"
+ },
+ {
+ "locale": "it",
+ "label": "Italiano"
+ },
+ {
+ "locale": "ja",
+ "label": "日本語"
+ },
+ {
+ "locale": "ko-kr",
+ "label": "한국어"
+ },
+ {
+ "locale": "ku",
+ "label": "کوردی"
+ },
+ {
+ "locale": "hi-in",
+ "label": "हिन्दी"
+ },
+ {
+ "locale": "hu",
+ "label": "Magyar"
+ },
+ {
+ "locale": "my",
+ "label": "မြန်မာစာ"
+ },
+ {
+ "locale": "ne",
+ "label": "नेपाली"
+ },
+ {
+ "locale": "no",
+ "label": "Norwegian"
+ },
+ {
+ "locale": "pl",
+ "label": "Polski"
+ },
+ {
+ "locale": "pt-br",
+ "label": "Português - Brasil"
+ },
+ {
+ "locale": "pt-pt",
+ "label": "Português - Europeu"
+ },
+ {
+ "locale": "ro",
+ "label": "Română"
+ },
+ {
+ "locale": "ru",
+ "label": "Russian"
+ },
+ {
+ "locale": "sl",
+ "label": "Slovenščina"
+ },
+ {
+ "locale": "sv",
+ "label": "Svenska"
+ },
+ {
+ "locale": "te",
+ "label": "తెలుగు"
+ },
+ {
+ "locale": "th",
+ "label": "ภาษาไทย"
+ },
+ {
+ "locale": "tr",
+ "label": "Türkçe"
+ },
+ {
+ "locale": "uk",
+ "label": "Ukrainian"
+ },
+ {
+ "locale": "vi",
+ "label": "Tiếng Việt"
+ },
+ {
+ "locale": "zh-cn",
+ "label": "简体中文"
+ },
+ {
+ "locale": "zh-tw",
+ "label": "繁體中文 (台灣)"
+ }
+]
diff --git a/apps/web/public/translations/main.json b/apps/web/public/translations/main.json
new file mode 100644
index 00000000..782c24d2
--- /dev/null
+++ b/apps/web/public/translations/main.json
@@ -0,0 +1,375 @@
+{
+ "action.convert-to-bookmark": "Convert to Bookmark",
+ "action.convert-to-embed": "Convert to Embed",
+ "action.open-embed-link": "Open link",
+ "action.align-bottom": "Align bottom",
+ "action.align-center-horizontal": "Align horizontally",
+ "action.align-center-vertical": "Align vertically",
+ "action.align-center-horizontal.short": "Align H",
+ "action.align-center-vertical.short": "Align V",
+ "action.align-left": "Align left",
+ "action.align-right": "Align right",
+ "action.align-top": "Align top",
+ "action.back-to-content": "Back to content",
+ "action.bring-forward": "Bring forward",
+ "action.bring-to-front": "Bring to front",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-json": "Copy as JSON",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-png": "Copy as PNG",
+ "action.copy-as-svg.short": "SVG",
+ "action.copy-as-svg": "Copy as SVG",
+ "action.copy": "Copy",
+ "action.cut": "Cut",
+ "action.delete": "Delete",
+ "action.unlock-all": "Unlock all",
+ "action.distribute-horizontal": "Distribute horizontally",
+ "action.distribute-vertical": "Distribute vertically",
+ "action.distribute-horizontal.short": "Distribute H",
+ "action.distribute-vertical.short": "Distribute V",
+ "action.duplicate": "Duplicate",
+ "action.edit-link": "Edit link",
+ "action.exit-pen-mode": "Exit pen mode",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-json": "Export as JSON",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-png": "Export as PNG",
+ "action.export-as-svg.short": "SVG",
+ "action.export-as-svg": "Export as SVG",
+ "action.export-all-as-json.short": "JSON",
+ "action.export-all-as-json": "Export as JSON",
+ "action.export-all-as-png.short": "PNG",
+ "action.export-all-as-png": "Export as PNG",
+ "action.export-all-as-svg.short": "SVG",
+ "action.export-all-as-svg": "Export as SVG",
+ "action.fit-frame-to-content": "Fit to content",
+ "action.flip-horizontal": "Flip horizontally",
+ "action.flip-vertical": "Flip vertically",
+ "action.flip-horizontal.short": "Flip H",
+ "action.flip-vertical.short": "Flip V",
+ "action.fork-project": "Fork this project",
+ "action.fork-project-on-tldraw": "Fork project on tldraw",
+ "action.group": "Group",
+ "action.insert-embed": "Insert embed",
+ "action.insert-media": "Upload media",
+ "action.leave-shared-project": "Leave shared project",
+ "action.new-project": "New project",
+ "action.new-shared-project": "New shared project",
+ "action.open-cursor-chat": "Cursor chat",
+ "action.open-file": "Open file",
+ "action.pack": "Pack",
+ "action.paste": "Paste",
+ "action.print": "Print",
+ "action.redo": "Redo",
+ "action.remove-frame": "Remove frame",
+ "action.rename": "Rename",
+ "action.rotate-ccw": "Rotate counterclockwise",
+ "action.rotate-cw": "Rotate clockwise",
+ "action.save-copy": "Save a copy",
+ "action.select-all": "Select all",
+ "action.select-none": "Select none",
+ "action.send-backward": "Send backward",
+ "action.send-to-back": "Send to back",
+ "action.share-project": "Share this project",
+ "action.stack-horizontal": "Stack horizontally",
+ "action.stack-vertical": "Stack vertically",
+ "action.stack-horizontal.short": "Stack H",
+ "action.stack-vertical.short": "Stack V",
+ "action.stop-following": "Stop following",
+ "action.stretch-horizontal": "Stretch horizontally",
+ "action.stretch-vertical": "Stretch vertically",
+ "action.stretch-horizontal.short": "Stretch H",
+ "action.stretch-vertical.short": "Stretch V",
+ "action.toggle-auto-size": "Toggle auto size",
+ "action.toggle-dark-mode.menu": "Dark mode",
+ "action.toggle-dark-mode": "Toggle dark mode",
+ "action.toggle-wrap-mode.menu": "Select on wrap",
+ "action.toggle-wrap-mode": "Toggle Select on wrap",
+ "action.toggle-reduce-motion.menu": "Reduce motion",
+ "action.toggle-reduce-motion": "Toggle reduce motion",
+ "action.toggle-edge-scrolling.menu": "Edge scrolling",
+ "action.toggle-edge-scrolling": "Toggle edge scrolling",
+ "action.toggle-debug-mode.menu": "Debug mode",
+ "action.toggle-debug-mode": "Toggle debug mode",
+ "action.toggle-focus-mode.menu": "Focus mode",
+ "action.toggle-focus-mode": "Toggle focus mode",
+ "action.toggle-grid.menu": "Show grid",
+ "action.toggle-grid": "Toggle grid",
+ "action.toggle-lock": "Toggle locked",
+ "action.toggle-snap-mode.menu": "Always snap",
+ "action.toggle-snap-mode": "Toggle always snap",
+ "action.toggle-tool-lock.menu": "Tool lock",
+ "action.toggle-tool-lock": "Toggle tool lock",
+ "action.toggle-transparent.context-menu": "Transparent",
+ "action.toggle-transparent.menu": "Transparent",
+ "action.toggle-transparent": "Toggle transparent background",
+ "action.undo": "Undo",
+ "action.ungroup": "Ungroup",
+ "action.zoom-in": "Zoom in",
+ "action.zoom-out": "Zoom out",
+ "action.zoom-to-100": "Zoom to 100%",
+ "action.zoom-to-fit": "Zoom to fit",
+ "action.zoom-to-selection": "Zoom to selection",
+ "assets.files.upload-failed": "Upload failed",
+ "assets.url.failed": "Couldn't load URL preview",
+ "color-style.white": "White",
+ "color-style.black": "Black",
+ "color-style.blue": "Blue",
+ "color-style.green": "Green",
+ "color-style.grey": "Grey",
+ "color-style.light-blue": "Light blue",
+ "color-style.light-green": "Light green",
+ "color-style.light-red": "Light red",
+ "color-style.light-violet": "Light violet",
+ "color-style.orange": "Orange",
+ "color-style.red": "Red",
+ "color-style.violet": "Violet",
+ "color-style.yellow": "Yellow",
+ "fill-style.none": "None",
+ "document.default-name": "Untitled",
+ "fill-style.semi": "Semi",
+ "fill-style.solid": "Solid",
+ "fill-style.pattern": "Pattern",
+ "dash-style.dashed": "Dashed",
+ "dash-style.dotted": "Dotted",
+ "dash-style.draw": "Draw",
+ "dash-style.solid": "Solid",
+ "size-style.s": "Small",
+ "size-style.m": "Medium",
+ "size-style.l": "Large",
+ "size-style.xl": "Extra large",
+ "opacity-style.0.1": "10%",
+ "opacity-style.0.25": "25%",
+ "opacity-style.0.5": "50%",
+ "opacity-style.0.75": "75%",
+ "opacity-style.1": "100%",
+ "font-style.draw": "Draw",
+ "font-style.sans": "Sans",
+ "font-style.serif": "Serif",
+ "font-style.mono": "Mono",
+ "align-style.start": "Start",
+ "align-style.middle": "Middle",
+ "align-style.end": "End",
+ "align-style.justify": "Justify",
+ "verticalAlign-style.start": "Top",
+ "verticalAlign-style.middle": "Middle",
+ "verticalAlign-style.end": "Bottom",
+ "geo-style.arrow-down": "Arrow down",
+ "geo-style.arrow-left": "Arrow left",
+ "geo-style.arrow-right": "Arrow right",
+ "geo-style.arrow-up": "Arrow up",
+ "geo-style.diamond": "Diamond",
+ "geo-style.ellipse": "Ellipse",
+ "geo-style.hexagon": "Hexagon",
+ "geo-style.octagon": "Octagon",
+ "geo-style.oval": "Oval",
+ "geo-style.cloud": "Cloud",
+ "geo-style.pentagon": "Pentagon",
+ "geo-style.rectangle": "Rectangle",
+ "geo-style.rhombus-2": "Rhombus 2",
+ "geo-style.rhombus": "Rhombus",
+ "geo-style.star": "Star",
+ "geo-style.trapezoid": "Trapezoid",
+ "geo-style.triangle": "Triangle",
+ "geo-style.x-box": "X box",
+ "geo-style.check-box": "Check box",
+ "arrowheadStart-style.none": "None",
+ "arrowheadStart-style.arrow": "Arrow",
+ "arrowheadStart-style.bar": "Bar",
+ "arrowheadStart-style.diamond": "Diamond",
+ "arrowheadStart-style.dot": "Dot",
+ "arrowheadStart-style.inverted": "Inverted",
+ "arrowheadStart-style.pipe": "Pipe",
+ "arrowheadStart-style.square": "Square",
+ "arrowheadStart-style.triangle": "Triangle",
+ "arrowheadEnd-style.none": "None",
+ "arrowheadEnd-style.arrow": "Arrow",
+ "arrowheadEnd-style.bar": "Bar",
+ "arrowheadEnd-style.diamond": "Diamond",
+ "arrowheadEnd-style.dot": "Dot",
+ "arrowheadEnd-style.inverted": "Inverted",
+ "arrowheadEnd-style.pipe": "Pipe",
+ "arrowheadEnd-style.square": "Square",
+ "arrowheadEnd-style.triangle": "Triangle",
+ "spline-style.line": "Line",
+ "spline-style.cubic": "Cubic",
+ "tool.select": "Select",
+ "tool.hand": "Hand",
+ "tool.draw": "Draw",
+ "tool.eraser": "Eraser",
+ "tool.arrow-down": "Arrow down",
+ "tool.arrow-left": "Arrow left",
+ "tool.arrow-right": "Arrow right",
+ "tool.arrow-up": "Arrow up",
+ "tool.arrow": "Arrow",
+ "tool.cloud": "Cloud",
+ "tool.diamond": "Diamond",
+ "tool.ellipse": "Ellipse",
+ "tool.hexagon": "Hexagon",
+ "tool.highlight": "Highlight",
+ "tool.line": "Line",
+ "tool.octagon": "Octagon",
+ "tool.oval": "Oval",
+ "tool.pentagon": "Pentagon",
+ "tool.rectangle": "Rectangle",
+ "tool.rhombus": "Rhombus",
+ "tool.star": "Star",
+ "tool.trapezoid": "Trapezoid",
+ "tool.triangle": "Triangle",
+ "tool.x-box": "X box",
+ "tool.check-box": "Check box",
+ "tool.asset": "Asset",
+ "tool.frame": "Frame",
+ "tool.note": "Note",
+ "tool.laser": "Laser",
+ "tool.embed": "Embed",
+ "tool.text": "Text",
+ "menu.title": "Menu",
+ "menu.copy-as": "Copy as",
+ "menu.edit": "Edit",
+ "menu.export-as": "Export as",
+ "menu.file": "File",
+ "menu.language": "Language",
+ "menu.preferences": "Preferences",
+ "menu.view": "View",
+ "context-menu.arrange": "Arrange",
+ "context-menu.copy-as": "Copy as",
+ "context-menu.export-as": "Export as",
+ "context-menu.export-all-as": "Export",
+ "context-menu.move-to-page": "Move to page",
+ "context-menu.reorder": "Reorder",
+ "page-menu.title": "Pages",
+ "page-menu.create-new-page": "Create new page",
+ "page-menu.max-page-count-reached": "Max pages reached",
+ "page-menu.new-page-initial-name": "Page 1",
+ "page-menu.edit-start": "Edit",
+ "page-menu.edit-done": "Done",
+ "page-menu.go-to-page": "Go to page",
+ "page-menu.submenu.rename": "Rename",
+ "page-menu.submenu.duplicate-page": "Duplicate",
+ "page-menu.submenu.title": "Menu",
+ "page-menu.submenu.move-down": "Move down",
+ "page-menu.submenu.move-up": "Move up",
+ "page-menu.submenu.delete": "Delete",
+ "share-menu.title": "Share",
+ "share-menu.save-note": "Download this project to your computer as a .tldr file.",
+ "share-menu.fork-note": "Create a new shared project based on this snapshot.",
+ "share-menu.share-project": "Share this project",
+ "share-menu.default-project-name": "Shared Project",
+ "share-menu.copy-link": "Copy editor link",
+ "share-menu.readonly-link": "Read-only",
+ "share-menu.create-snapshot-link": "Copy snapshot link",
+ "share-menu.snapshot-link-note": "Capture and share this project as a read-only snapshot link.",
+ "share-menu.copy-readonly-link": "Copy viewer link",
+ "share-menu.offline-note": "Create a new shared project based on your current project.",
+ "share-menu.copy-link-note": "Anyone with the link will be able to view and edit this project.",
+ "share-menu.copy-readonly-link-note": "Anyone with the link will be able to access this project.",
+ "share-menu.project-too-large": "Sorry, this project can't be shared because it's too large. We're working on it!",
+ "share-menu.upload-failed": "Sorry, we couldn't upload your project at the moment. Please try again or let us know if the problem persists.",
+ "share-menu.creating-project": "Creating the new project…",
+ "share-menu.copied": "Copied link",
+ "status.offline": "Offline",
+ "status.online": "Online",
+ "people-menu.title": "People",
+ "people-menu.change-name": "Change name",
+ "people-menu.change-color": "Change color",
+ "people-menu.follow": "Following",
+ "people-menu.following": "Following",
+ "people-menu.leading": "Following You",
+ "people-menu.user": "(You)",
+ "people-menu.invite": "Invite others",
+ "help-menu.title": "Help and resources",
+ "help-menu.about": "About",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "Keyboard shortcuts",
+ "help-menu.twitter": "Twitter",
+ "actions-menu.title": "Actions",
+ "edit-link-dialog.title": "Edit link",
+ "edit-link-dialog.invalid-url": "A link must be a valid URL.",
+ "edit-link-dialog.detail": "Links will open in a new tab.",
+ "edit-link-dialog.url": "URL",
+ "edit-link-dialog.clear": "Clear",
+ "edit-link-dialog.save": "Continue",
+ "edit-link-dialog.cancel": "Cancel",
+ "embed-dialog.title": "Insert embed",
+ "embed-dialog.back": "Back",
+ "embed-dialog.create": "Create",
+ "embed-dialog.cancel": "Cancel",
+ "embed-dialog.url": "URL",
+ "embed-dialog.instruction": "Paste in the site's URL to create the embed.",
+ "embed-dialog.invalid-url": "We could not create an embed from that URL.",
+ "edit-pages-dialog.move-down": "Move down",
+ "edit-pages-dialog.move-up": "Move up",
+ "shortcuts-dialog.title": "Keyboard shortcuts",
+ "shortcuts-dialog.edit": "Edit",
+ "shortcuts-dialog.file": "File",
+ "shortcuts-dialog.preferences": "Preferences",
+ "shortcuts-dialog.tools": "Tools",
+ "shortcuts-dialog.transform": "Transform",
+ "shortcuts-dialog.view": "View",
+ "shortcuts-dialog.collaboration": "Collaboration",
+ "home-project-dialog.title": "Home project",
+ "home-project-dialog.description": "This is your local home project. It's just for you!",
+ "rename-project-dialog.title": "Rename project",
+ "rename-project-dialog.cancel": "Cancel",
+ "rename-project-dialog.rename": "Rename",
+ "home-project-dialog.ok": "Ok",
+ "style-panel.title": "Styles",
+ "style-panel.align": "Align",
+ "style-panel.vertical-align": "Vertical align",
+ "style-panel.position": "Position",
+ "style-panel.arrowheads": "Arrows",
+ "style-panel.arrowhead-start": "Start",
+ "style-panel.arrowhead-end": "End",
+ "style-panel.color": "Color",
+ "style-panel.dash": "Dash",
+ "style-panel.fill": "Fill",
+ "style-panel.font": "Font",
+ "style-panel.geo": "Shape",
+ "style-panel.mixed": "Mixed",
+ "style-panel.opacity": "Opacity",
+ "style-panel.size": "Size",
+ "style-panel.spline": "Spline",
+ "tool-panel.drawing": "Drawing",
+ "tool-panel.shapes": "Shapes",
+ "tool-panel.more": "More",
+ "debug-panel.more": "More",
+ "navigation-zone.toggle-minimap": "Toggle minimap",
+ "navigation-zone.zoom": "Zoom",
+ "focus-mode.toggle-focus-mode": "Toggle focus mode",
+ "toast.close": "Close",
+ "file-system.file-open-error.title": "Could not open file",
+ "file-system.file-open-error.not-a-tldraw-file": "The file you tried to open doesn't look like a tldraw file.",
+ "file-system.file-open-error.file-format-version-too-new": "The file you tried to open is from a newer version of tldraw. Please reload the page and try again.",
+ "file-system.file-open-error.generic-corrupted-file": "The file you tried to open is corrupted.",
+ "file-system.confirm-open.title": "Overwrite current project?",
+ "file-system.confirm-open.description": "Opening a file will replace your current project and any unsaved changes will be lost. Are you sure you want to continue?",
+ "file-system.confirm-open.cancel": "Cancel",
+ "file-system.confirm-open.open": "Open file",
+ "file-system.confirm-open.dont-show-again": "Don't ask again",
+ "file-system.confirm-clear.title": "Clear current project?",
+ "file-system.confirm-clear.description": "Creating a new project will clear your current project and any unsaved changes will be lost. Are you sure you want to continue?",
+ "file-system.confirm-clear.cancel": "Cancel",
+ "file-system.confirm-clear.continue": "Continue",
+ "file-system.confirm-clear.dont-show-again": "Don't ask again",
+ "file-system.shared-document-file-open-error.title": "Could not open file",
+ "file-system.shared-document-file-open-error.description": "Opening files from shared projects is not supported.",
+ "sharing.confirm-leave.title": "Leave current project?",
+ "sharing.confirm-leave.description": "Are you sure you want to leave this shared project? You can return to it by navigating to its URL.",
+ "sharing.confirm-leave.cancel": "Cancel",
+ "sharing.confirm-leave.leave": "Leave",
+ "sharing.confirm-leave.dont-show-again": "Don't ask again",
+ "toast.error.export-fail.title": "Failed export",
+ "toast.error.export-fail.desc": "Failed to export image",
+ "toast.error.copy-fail.title": "Failed copy",
+ "toast.error.copy-fail.desc": "Failed to copy image",
+ "context.pages.new-page": "New page",
+ "vscode.file-open.desc": "We've updated this document to work with the current version of tldraw. If you'd like to keep the original version (which will work on old.tldraw.com), click below to create a backup.",
+ "vscode.file-open.open": "Continue",
+ "vscode.file-open.backup": "Backup",
+ "vscode.file-open.backup-saved": "Backup saved",
+ "vscode.file-open.backup-failed": "Backup failed: this is not a .tldr file.",
+ "vscode.file-open.dont-show-again": "Don't ask again",
+ "cursor-chat.type-to-chat": "Type to chat..."
+}
diff --git a/apps/web/public/translations/my.json b/apps/web/public/translations/my.json
new file mode 100644
index 00000000..9dae7a1f
--- /dev/null
+++ b/apps/web/public/translations/my.json
@@ -0,0 +1,112 @@
+{
+ "action.align-bottom": "အောက်သို့ ညှိရန်",
+ "action.align-center-horizontal": "အလျားလိုက် ဗဟိုဆီသို့ ညှိရန်",
+ "action.align-center-vertical": "ဒေါင်လိုက် ဗဟိုဆီသို့ ညှိရန်",
+ "action.align-center-horizontal.short": "အလျားလိုက် ဗဟိုဆီသို့ ညှိရန်",
+ "action.align-center-vertical.short": "ဒေါင်လိုက် ဗဟိုဆီသို့ ညှိရန်",
+ "action.align-left": "ဘယ်ဖက်သို့ ညှိရန်",
+ "action.align-right": "ညာဖက်သို့ ညှိရန်",
+ "action.align-top": "အပေါ်သို့ ညှိရန်",
+ "action.bring-forward": "ရှေ့သို့ တစ်ဆင့်ပို့မည်",
+ "action.bring-to-front": "ရှေ့ဆုံးသို့ ပို့မည်",
+ "action.copy": "ကူးယူ",
+ "action.cut": "ဖြတ်ယူ",
+ "action.delete": "ဖျက်မည်",
+ "action.distribute-horizontal": "အလျားလိုက် ဖြန့်ရန်",
+ "action.distribute-vertical": "ဒေါင်လိုက် ဖြန့်ရန်",
+ "action.distribute-horizontal.short": "အလျားလိုက် ဖြန့်ရန်",
+ "action.distribute-vertical.short": "ဒေါင်လိုက် ဖြန့်ရန်",
+ "action.duplicate": "ပွားမည်",
+ "action.flip-horizontal": "အလျားလိုက် လှန်မည်",
+ "action.flip-vertical": "ဒေါင်လိုက် လှန်မည်",
+ "action.flip-horizontal.short": "အလျားလိုက် လှန်မည်",
+ "action.flip-vertical.short": "ဒေါင်လိုက် လှန်မည်",
+ "action.group": "အုပ်စုဖွဲ့",
+ "action.insert-media": "မီဒီယာဖိုင်များ တင်မည်",
+ "action.paste": "ကူးသွင်း",
+ "action.redo": "ပြန်လုပ်ရန်",
+ "action.select-all": "အားလုံးကို ရွေးချယ်ရန်",
+ "action.select-none": "တစ်ခုမှ မရွေးတော့ပါ",
+ "action.send-backward": "နောက်သို့ တစ်ဆင့်ပို့မည်",
+ "action.send-to-back": "နောက်ဆုံးသို့ ပို့မည်",
+ "action.stretch-horizontal": "အလျားလိုက် ဆွဲဆန့်ရန်",
+ "action.stretch-vertical": "ဒေါင်လိုက် ဆွဲဆန့်ရန်",
+ "action.stretch-horizontal.short": "အလျားလိုက် ဆွဲဆန့်ရန်",
+ "action.stretch-vertical.short": "ဒေါင်လိုက် ဆွဲဆန့်ရန်",
+ "action.toggle-dark-mode.menu": "အမှောင် မုဒ်",
+ "action.toggle-dark-mode": "အမှောင် မုဒ်",
+ "action.toggle-debug-mode.menu": "စမ်းသပ် မုဒ်",
+ "action.toggle-debug-mode": "စမ်းသပ် မုဒ်",
+ "action.toggle-focus-mode.menu": "ရှင်းရှင်းလင်းလင်း မုဒ်",
+ "action.toggle-focus-mode": "ရှင်းရှင်းလင်းလင်း မုဒ်",
+ "action.toggle-grid.menu": "နောက်ခံ ဇယားကွက်ပြရန်",
+ "action.toggle-grid": "နောက်ခံ ဇယားကွက်ပြရန်",
+ "action.toggle-snap-mode.menu": "Always Show Snaps",
+ "action.toggle-snap-mode": "Always Show Snaps",
+ "action.toggle-transparent.context-menu": "နောက်ခံ အကြည်",
+ "action.toggle-transparent.menu": "နောက်ခံ အကြည်",
+ "action.undo": "နဂိုမူလသို့ ပြန်လုပ်ရန်",
+ "action.ungroup": "အုပ်စုခွဲ",
+ "action.zoom-in": "အကြီးချဲ့မည်",
+ "action.zoom-out": "ပြန်ကျဥ်းမည်",
+ "action.zoom-to-fit": "အံကိုက်ဖြစ်အောင် ချဲ့မည်",
+ "action.zoom-to-selection": "ရွေးထားသော နေရာသို့ အာရုံပြုမည်",
+ "dash-style.draw": "ခဲတံ",
+ "font-style.draw": "ခဲတံ",
+ "geo-style.ellipse": "ဘဲဥ",
+ "geo-style.rectangle": "လေထောင့်",
+ "geo-style.triangle": "တြိဂံ",
+ "arrowheadStart-style.arrow": "မြှား",
+ "arrowheadStart-style.triangle": "တြိဂံ",
+ "arrowheadEnd-style.arrow": "မြှား",
+ "arrowheadEnd-style.triangle": "တြိဂံ",
+ "spline-style.line": "မျဥ်း",
+ "tool.select": "ရွေးချယ်မှု",
+ "tool.draw": "ခဲတံ",
+ "tool.eraser": "ခဲဖျက်",
+ "tool.arrow": "မြှား",
+ "tool.ellipse": "ဘဲဥ",
+ "tool.line": "မျဥ်း",
+ "tool.rectangle": "လေထောင့်",
+ "tool.triangle": "တြိဂံ",
+ "tool.note": "ကပ်ခွာမှတ်စု",
+ "tool.text": "စာသား",
+ "menu.copy-as": "ကူးယူမည့် ပုံစံ",
+ "menu.edit": "ပြုပြင်",
+ "menu.export-as": "ထုတ်ချင်သည့် ပုံစံ",
+ "menu.file": "ဖိုင်",
+ "menu.language": "ဘာသာစကား",
+ "menu.preferences": "ဆက်တင်",
+ "menu.view": "အမြင်",
+ "context-menu.copy-as": "ကူးယူမည့် ပုံစံ",
+ "context-menu.export-as": "ထုတ်ချင်သည့် ပုံစံ",
+ "context-menu.move-to-page": "စာမျက်နှာတစ်ခုသို့ ရွေးမည်",
+ "page-menu.create-new-page": "စာမျက်နှာ အသစ်ဖွင့်မည်",
+ "page-menu.edit-start": "ပြုပြင်",
+ "page-menu.submenu.duplicate-page": "ပွားမည်",
+ "page-menu.submenu.delete": "ဖျက်မည်",
+ "share-menu.copy-link": "ဖိတ်ကြားရန် လင့်ခ်ကူးယူမည်",
+ "share-menu.copy-readonly-link": "ကြည့်ရူရန်အတွက်သာ လင့်ခ်ကူးယူမည်",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "Keyboard shortcuts",
+ "help-menu.twitter": "Twitter",
+ "edit-link-dialog.cancel": "မလုပ်တော့ပါ",
+ "embed-dialog.cancel": "မလုပ်တော့ပါ",
+ "shortcuts-dialog.title": "Keyboard shortcuts",
+ "shortcuts-dialog.edit": "ပြုပြင်",
+ "shortcuts-dialog.file": "ဖိုင်",
+ "shortcuts-dialog.preferences": "ဆက်တင်",
+ "shortcuts-dialog.tools": "ကိရိယာများ",
+ "shortcuts-dialog.transform": "ပြောင်းလည်ရန်",
+ "shortcuts-dialog.view": "အမြင်",
+ "style-panel.title": "စတိုင်",
+ "style-panel.align": "အထားအသို",
+ "style-panel.color": "အရောင်",
+ "style-panel.dash": "မျဥ်းစက်",
+ "style-panel.fill": "အရောင် ထည့်မည်",
+ "style-panel.font": "စာသားဖောင့်",
+ "style-panel.size": "အရွယ်အစား",
+ "focus-mode.toggle-focus-mode": "ရှင်းရှင်းလင်းလင်း မုဒ်",
+ "file-system.confirm-open.cancel": "မလုပ်တော့ပါ"
+}
diff --git a/apps/web/public/translations/ne.json b/apps/web/public/translations/ne.json
new file mode 100644
index 00000000..ccf5e8d2
--- /dev/null
+++ b/apps/web/public/translations/ne.json
@@ -0,0 +1,295 @@
+{
+ "action.align-bottom": "तल पङ्क्तिबद्ध गर्नुहोस्",
+ "action.align-center-horizontal": "तेर्सो रूपमा पङ्क्तिबद्ध गर्नुहोस्",
+ "action.align-center-horizontal.short": "तेर्सो रूपमा पङ्क्तिबद्ध गर्नुहोस्",
+ "action.align-center-vertical": "ठाडो रूपमा पङ्क्तिबद्ध गर्नुहोस्",
+ "action.align-center-vertical.short": "ठाडो रूपमा पङ्क्तिबद्ध गर्नुहोस्",
+ "action.align-left": "बायाँ पङ्क्तिबद्ध गर्नुहोस्",
+ "action.align-right": "दायाँ पङ्क्तिबद्ध गर्नुहोस्",
+ "action.align-top": "माथि पङ्क्तिबद्ध गर्नुहोस्",
+ "action.back-to-content": "सामग्रीमा फर्कनुहोस्",
+ "action.bring-forward": "अगाडि ल्याउनुहोस्",
+ "action.bring-to-front": "सबैभन्दा अगाडि ल्याउनुहोस्",
+ "action.convert-to-bookmark": "बुकमार्कमा रूपान्तरण गर्नुहोस्",
+ "action.convert-to-embed": "एम्बेडमा रूपान्तरण गर्नुहोस्",
+ "action.copy": "कपि गर्नुहोस्",
+ "action.copy-as-json": "JSON को रूपमा प्रतिलिपि गर्नुहोस्",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "PNG को रूपमा प्रतिलिपि गर्नुहोस्",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "SVG को रूपमा प्रतिलिपि गर्नुहोस्",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "कट गर्नुहोस्",
+ "action.delete": "मेटाउनुहोस्",
+ "action.distribute-horizontal": "तेर्सो रूपमा वितरण गर्नुहोस्",
+ "action.distribute-horizontal.short": "तेर्सो रूपमा वितरण गर्नुहोस्",
+ "action.distribute-vertical": "ठाडो रूपमा वितरण गर्नुहोस्",
+ "action.distribute-vertical.short": "ठाडो रूपमा वितरण गर्नुहोस्",
+ "action.duplicate": "अनुलिपि गर्नुहोस्",
+ "action.edit-link": "लिङ्क सम्पादन गर्नुहोस्",
+ "action.exit-pen-mode": "कलम मोडबाट बाहिर निस्कनुहोस्",
+ "action.export-as-json": "JSON को रूपमा निर्यात गर्नुहोस्",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "PNG को रूपमा निर्यात गर्नुहोस्",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "SVG को रूपमा निर्यात गर्नुहोस्",
+ "action.export-as-svg.short": "SVG",
+ "action.flip-horizontal": "तेर्सो फ्लिप गर्नुहोस्",
+ "action.flip-horizontal.short": "तेर्सो रूपमा फ्लिप गर्नुहोस्",
+ "action.flip-vertical": "ठाडो फ्लिप गर्नुहोस्",
+ "action.flip-vertical.short": "ठाडो रूपमा फ्लिप गर्नुहोस्",
+ "action.group": "समूह",
+ "action.insert-media": "मिडिया अपलोड गर्नुहोस्",
+ "action.new-shared-project": "नयाँ साझा परियोजना",
+ "action.open-embed-link": "लिङ्क खोल्नुहोस्",
+ "action.open-file": "फाइल खोल्नुहोस्",
+ "action.pack": "प्याक",
+ "action.paste": "पेस्ट गर्नुहोस्",
+ "action.print": "मुद्रण गर्नुहोस्",
+ "action.redo": "पुनः गर्नुहोस्",
+ "action.rotate-ccw": "घडीको विपरीत दिशामा घुमाउनुहोस्",
+ "action.rotate-cw": "घडीको दिशामा घुमाउनुहोस्",
+ "action.save-copy": "एक प्रतिलिपि बचत गर्नुहोस्",
+ "action.select-all": "सबै छान्नुहोस्",
+ "action.select-none": "केहि पनि सेलेक्ट नगर्नुहोस्",
+ "action.send-backward": "पछाडि पठाउनुहोस्",
+ "action.send-to-back": "सबैभन्दा पछाडि पठाउनुहोस्",
+ "action.share-project": "यो परियोजना साझेदारी गर्नुहोस्",
+ "action.stack-horizontal": "तेर्सो रूपमा स्ट्याक गर्नुहोस्",
+ "action.stack-horizontal.short": "तेर्सो रूपमा स्ट्याक गर्नुहोस्",
+ "action.stack-vertical": "ठाडो रूपमा स्ट्याक गर्नुहोस्",
+ "action.stack-vertical.short": "ठाडो रूपमा स्ट्याक गर्नुहोस्",
+ "action.stretch-horizontal": "तेर्सो रूपमा तन्काउनुहोस्",
+ "action.stretch-horizontal.short": "तेर्सो रूपमा तन्काउनुहोस्",
+ "action.stretch-vertical": "ठाडो रूपमा तन्काउनुहोस्",
+ "action.stretch-vertical.short": "ठाडो रूपमा तन्काउनुहोस्",
+ "action.toggle-auto-size": "स्वत: आकार टगल गर्नुहोस्",
+ "action.toggle-dark-mode": "अँध्यारो मोड टगल गर्नुहोस्",
+ "action.toggle-dark-mode.menu": "अँध्यारो मोड",
+ "action.toggle-debug-mode": "डिबग मोड टगल गर्नुहोस्",
+ "action.toggle-debug-mode.menu": "डिबग मोड",
+ "action.toggle-focus-mode": "फोकस मोड टगल गर्नुहोस्",
+ "action.toggle-focus-mode.menu": "फोकस मोड",
+ "action.toggle-grid": "ग्रिड टगल गर्नुहोस्",
+ "action.toggle-grid.menu": "ग्रिड देखाउनुहोस्",
+ "action.toggle-snap-mode": "जहिले पनि स्न्याप गर्ने टगल गर्नुहोस्",
+ "action.toggle-snap-mode.menu": "जहिले पनि स्न्याप गर्नुहोस्",
+ "action.toggle-tool-lock": "उपकरण लक टगल गर्नुहोस्",
+ "action.toggle-tool-lock.menu": "उपकरण लक",
+ "action.toggle-transparent": "पारदर्शी पृष्ठभूमि टगल गर्नुहोस्",
+ "action.toggle-transparent.context-menu": "पारदर्शी",
+ "action.toggle-transparent.menu": "पारदर्शी",
+ "action.undo": "पूर्ववत गर्नुहोस्",
+ "action.ungroup": "समूह रद्द गर्नुहोस्",
+ "action.zoom-in": "जुम इन",
+ "action.zoom-out": "जुम आउट",
+ "action.zoom-to-100": "१००% मा जुम गर्नुहोस्",
+ "action.zoom-to-fit": "जुम टु फिट",
+ "action.zoom-to-selection": "जुम टु सेलेक्सन",
+ "actions-menu.title": "कार्यहरू",
+ "align-style.end": "अन्त्य",
+ "align-style.justify": "उचित",
+ "align-style.middle": "मध्य",
+ "align-style.start": "सुरु",
+ "arrowheadEnd-style.arrow": "तीर",
+ "arrowheadEnd-style.bar": "बार",
+ "arrowheadEnd-style.diamond": "हीरा",
+ "arrowheadEnd-style.dot": "डट",
+ "arrowheadEnd-style.inverted": "उल्टो",
+ "arrowheadEnd-style.none": "कुनै पनि नाई",
+ "arrowheadEnd-style.pipe": "पाइप",
+ "arrowheadEnd-style.square": "वर्ग",
+ "arrowheadEnd-style.triangle": "त्रिभुज",
+ "arrowheadStart-style.arrow": "तीर",
+ "arrowheadStart-style.bar": "बार",
+ "arrowheadStart-style.diamond": "हीरा",
+ "arrowheadStart-style.dot": "डट",
+ "arrowheadStart-style.inverted": "उल्टो",
+ "arrowheadStart-style.none": "कुनै पनि नाई",
+ "arrowheadStart-style.pipe": "पाइप",
+ "arrowheadStart-style.square": "वर्ग",
+ "arrowheadStart-style.triangle": "त्रिभुज",
+ "color-style.black": "कालो",
+ "color-style.blue": "निलो",
+ "color-style.green": "हरियो",
+ "color-style.grey": "खैरो",
+ "color-style.light-blue": "हल्का निलो",
+ "color-style.light-green": "हल्का हरियो",
+ "color-style.light-red": "हल्का रातो",
+ "color-style.light-violet": "हल्का बैंगनी",
+ "color-style.orange": "सुन्तला",
+ "color-style.red": "रातो",
+ "color-style.violet": "बैंगनी",
+ "color-style.yellow": "पहेंलो",
+ "context-menu.arrange": "व्यवस्थित गर्नुहोस्",
+ "context-menu.copy-as": "कपि एज",
+ "context-menu.export-as": "एक्सपोर्ट एज",
+ "context-menu.move-to-page": "पृष्ठमा सार्नुहोस्",
+ "context-menu.reorder": "पुन: क्रमबद्ध गर्नुहोस्",
+ "context.pages.new-page": "नयाँ पृष्ठ",
+ "dash-style.dashed": "ड्यास गरिएको",
+ "dash-style.dotted": "डट गरिएको",
+ "dash-style.draw": "चित्र बनाउनु",
+ "dash-style.solid": "ठोस",
+ "edit-link-dialog.cancel": "रद्द",
+ "edit-link-dialog.clear": "खाली गर्नुहोस्",
+ "edit-link-dialog.detail": "लिङ्कहरू नयाँ ट्याबमा खुल्नेछन्।",
+ "edit-link-dialog.invalid-url": "लिङ्क एक मान्य URL हुनुपर्छ।",
+ "edit-link-dialog.save": "जारी राख्नुहोस्",
+ "edit-link-dialog.title": "लिङ्क सम्पादन गर्नुहोस्",
+ "edit-link-dialog.url": "URL",
+ "edit-pages-dialog.move-down": "तल सार्नुहोस्",
+ "edit-pages-dialog.move-up": "माथि सार्नुहोस्",
+ "embed-dialog.back": "पछाडी जाउ",
+ "embed-dialog.cancel": "रद्द",
+ "embed-dialog.create": "सिर्जना गर्नुहोस्",
+ "embed-dialog.instruction": "इम्बेड सिर्जना गर्न साइटको URL मा टाँस्नुहोस्।",
+ "embed-dialog.invalid-url": "हामीले त्यो URL बाट एम्बेड सिर्जना गर्न सकेनौं।",
+ "embed-dialog.title": "इम्बेड सिर्जना गर्नुहोस्",
+ "embed-dialog.url": "URL",
+ "file-system.confirm-open.cancel": "रद्द",
+ "file-system.confirm-open.description": "फाइल खोल्दा तपाईंको हालको परियोजना प्रतिस्थापन हुनेछ र कुनै पनि बचत नगरिएका परिवर्तनहरू हराउनेछन्। के तपाइँ यहि चाहनुहुन्छ?",
+ "file-system.confirm-open.dont-show-again": "फेरि नसोध्नुहोस्",
+ "file-system.confirm-open.open": "फाइल खोल्नुहोस्",
+ "file-system.confirm-open.title": "हालको परियोजना अधिलेखन गर्ने?",
+ "file-system.file-open-error.file-format-version-too-new": "तपाईंले खोल्न खोजेको फाइल tldraw को नयाँ संस्करणबाट हो। कृपया पृष्ठ पुन: लोड गर्नुहोस् र पुन: प्रयास गर्नुहोस्।",
+ "file-system.file-open-error.generic-corrupted-file": "तपाईंले खोल्ने प्रयास गर्नुभएको फाइल बिग्रिएको छ।",
+ "file-system.file-open-error.not-a-tldraw-file": "तपाईंले खोल्न खोजेको फाइल tldraw फाइल जस्तो देखिदैन।",
+ "file-system.file-open-error.title": "फाइल खोल्न सकिएन",
+ "file-system.shared-document-file-open-error.description": "साझा परियोजनाहरूबाट फाइलहरू खोल्न समर्थित छैन।",
+ "file-system.shared-document-file-open-error.title": "फाइल खोल्न सकिएन",
+ "fill-style.none": "कुनै पनि नाई",
+ "fill-style.pattern": "ढाँचा",
+ "fill-style.semi": "अर्ध",
+ "fill-style.solid": "ठोस",
+ "focus-mode.toggle-focus-mode": "फोकस मोड टगल गर्नुहोस्",
+ "font-style.draw": "चित्र बनाउनु",
+ "font-style.mono": "मोनो",
+ "font-style.sans": "सांस",
+ "font-style.serif": "सेरिफ",
+ "geo-style.arrow-down": "बाण तल",
+ "geo-style.arrow-left": "बाण बायाँ",
+ "geo-style.arrow-right": "बाण दायाँ",
+ "geo-style.arrow-up": "बाण माथि",
+ "geo-style.diamond": "हीरा",
+ "geo-style.ellipse": "दीर्घवृत्त",
+ "geo-style.hexagon": "हेक्सागन",
+ "geo-style.octagon": "अष्टकोण",
+ "geo-style.oval": "ओवल",
+ "geo-style.pentagon": "पेन्टागन",
+ "geo-style.rectangle": "आयत",
+ "geo-style.rhombus": "रोम्बस",
+ "geo-style.rhombus-2": "रोम्बस २",
+ "geo-style.star": "तारा",
+ "geo-style.trapezoid": "Trapezoid",
+ "geo-style.triangle": "त्रिभुज",
+ "geo-style.x-box": "X बाकस",
+ "help-menu.about": "tldraw को बारे मा",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "किबोर्ड सर्टकटहरू",
+ "help-menu.title": "मद्दत र स्रोतहरू",
+ "help-menu.twitter": "ट्विटर",
+ "menu.copy-as": "कपि एज",
+ "menu.edit": "सम्पादन गर्नुहोस्",
+ "menu.export-as": "एक्सपोर्ट एज",
+ "menu.file": "फाइल",
+ "menu.language": "भाषा",
+ "menu.preferences": "प्राथमिकताहरू",
+ "menu.title": "मेनु",
+ "menu.view": "भ्यू",
+ "navigation-zone.toggle-minimap": "मिनिम्याप टगल गर्नुहोस्",
+ "navigation-zone.zoom": "जुम",
+ "opacity-style.0.1": "१०%",
+ "opacity-style.0.25": "२५%",
+ "opacity-style.0.5": "५०%",
+ "opacity-style.0.75": "७५%",
+ "opacity-style.1": "१००%",
+ "page-menu.create-new-page": "नयाँ पृष्ठ सिर्जना गर्नुहोस्",
+ "page-menu.edit-done": "सकियो",
+ "page-menu.edit-start": "सम्पादन",
+ "page-menu.max-page-count-reached": "अधिकतम पृष्ठ पुग्यो",
+ "page-menu.new-page-initial-name": "पृष्ठ १",
+ "page-menu.submenu.delete": "मेटाउनुहोस्",
+ "page-menu.submenu.duplicate-page": "नक्कल",
+ "page-menu.submenu.move-down": "तल सार्नुहोस्",
+ "page-menu.submenu.move-up": "माथि सार्नुहोस्",
+ "page-menu.submenu.rename": "पुन: नामाकरण",
+ "page-menu.submenu.title": "मेनु",
+ "page-menu.title": "पृष्ठहरू",
+ "people-menu.change-color": "रङ परिवर्तन गर्नुहोस्",
+ "people-menu.change-name": "नाम परिवर्तन गर्नुहोस्",
+ "people-menu.invite": "अरूलाई निमन्त्रणा गर्नुहोस्",
+ "people-menu.title": "मानिस",
+ "people-menu.user": "(तिमी)",
+ "share-menu.copy-link": "लिङ्क प्रतिलिपि गर्नुहोस्",
+ "share-menu.copy-link-note": "लिङ्क भएका जो कोहीले पनि यो परियोजना हेर्न र सम्पादन गर्न सक्षम हुनेछन्।",
+ "share-menu.copy-readonly-link": "पढ्ने मात्र लिङ्क प्रतिलिपि गर्नुहोस्",
+ "share-menu.copy-readonly-link-note": "लिङ्क भएको कुनै पनि व्यक्तिले यो परियोजना हेर्न (तर सम्पादन गर्दैन) सक्षम हुनेछ।",
+ "share-menu.offline-note": "यो परियोजना सेयर गर्नाले नयाँ URL मा होस्ट गरिएको लाइभ प्रतिलिपि सिर्जना गर्नेछ। तपाईंले परियोजना हेर्न र सम्पादन गर्नका लागि तीस अन्य व्यक्तिहरूसँग URL सेयर गर्न सक्नुहुन्छ।",
+ "share-menu.project-too-large": "माफ गर्नुहोस्, यो परियोजना सेयर गर्न सकिँदैन किनभने यो धेरै ठूलो छ। हामी यसमा काम गर्दैछौं!",
+ "share-menu.readonly-link": "पढ्नका लागि मात्र",
+ "share-menu.share-project": "यो परियोजना सेयर गर्नुहोस्",
+ "share-menu.title": "सेयर गर्नुहोस्",
+ "shortcuts-dialog.edit": "सम्पादन गर्नुहोस्",
+ "shortcuts-dialog.file": "फाइल",
+ "shortcuts-dialog.preferences": "प्राथमिकताहरू",
+ "shortcuts-dialog.title": "किबोर्ड सर्टकटहरू",
+ "shortcuts-dialog.tools": "उपकरण",
+ "shortcuts-dialog.transform": "रूपान्तरण",
+ "shortcuts-dialog.view": "दृश्य",
+ "size-style.l": "ठूलो",
+ "size-style.m": "मध्यम",
+ "size-style.s": "सानो",
+ "size-style.xl": "धेरै ठूलाे",
+ "spline-style.cubic": "घन",
+ "spline-style.line": "रेखा",
+ "style-panel.align": "पङ्क्तिबद्ध",
+ "style-panel.arrowheads": "एरोहेडहरू",
+ "style-panel.color": "रंग",
+ "style-panel.dash": "धर्का",
+ "style-panel.fill": "भर्नुहोस्",
+ "style-panel.font": "फन्ट",
+ "style-panel.geo": "आकृति",
+ "style-panel.mixed": "मिश्रित",
+ "style-panel.opacity": "अस्पष्टता",
+ "style-panel.size": "आकार",
+ "style-panel.spline": "स्प्लाइन",
+ "style-panel.title": "शैलीहरू",
+ "toast.close": "बन्द",
+ "toast.error.copy-fail.desc": "छवि प्रतिलिपि गर्न असफल भयो",
+ "toast.error.copy-fail.title": "प्रतिलिपि असफल भयो",
+ "toast.error.export-fail.desc": "छवि निर्यात गर्न असफल भयो",
+ "toast.error.export-fail.title": "निर्यात गर्न असफल भयो",
+ "tool-panel.drawing": "रेखाचित्र",
+ "tool-panel.shapes": "आकृतिहरू",
+ "tool.arrow": "तीर",
+ "tool.arrow-down": "बाण तल",
+ "tool.arrow-left": "बाण बायाँ",
+ "tool.arrow-right": "बाण दायाँ",
+ "tool.arrow-up": "बाण माथि",
+ "tool.asset": "सम्पत्ति",
+ "tool.diamond": "हीरा",
+ "tool.draw": "चित्र बनाउनु",
+ "tool.ellipse": "दीर्घवृत्त",
+ "tool.embed": "इम्बेड गर्नुहोस्",
+ "tool.eraser": "इरेजर",
+ "tool.frame": "फ्रेम",
+ "tool.hand": "हात",
+ "tool.hexagon": "हेक्सागन",
+ "tool.line": "रेखा",
+ "tool.note": "टाँसिने",
+ "tool.octagon": "अष्टकोण",
+ "tool.oval": "ओवल",
+ "tool.pentagon": "पेन्टागन",
+ "tool.rectangle": "आयत",
+ "tool.rhombus": "रोम्बस",
+ "tool.select": "सेलेक्ट",
+ "tool.star": "तारा",
+ "tool.text": "शब्द",
+ "tool.trapezoid": "Trapezoid",
+ "tool.triangle": "त्रिभुज",
+ "tool.x-box": "एक्स बाकस",
+ "vscode.file-open.desc": "यो फाइल tldraw को पुरानो संस्करणबाट सिर्जना गरिएको थियो। के तपाइँ नयाँ संस्करण संग काम गर्न यसलाई upgrade गर्न चाहनुहुन्छ?",
+ "vscode.file-open.dont-show-again": "फेरि नसोध्नुहोस्"
+}
diff --git a/apps/web/public/translations/no.json b/apps/web/public/translations/no.json
new file mode 100644
index 00000000..284da7de
--- /dev/null
+++ b/apps/web/public/translations/no.json
@@ -0,0 +1,62 @@
+{
+ "action.copy": "Kopier",
+ "action.cut": "Klipp ut",
+ "action.delete": "Slett",
+ "action.duplicate": "Dupliser",
+ "action.flip-horizontal": "Snu horisontalt",
+ "action.flip-vertical": "Snu vertikalt",
+ "action.group": "Grupper",
+ "action.insert-media": "Last opp media",
+ "action.paste": "Lim inn",
+ "action.redo": "Gjør om",
+ "action.select-all": "Velg alle",
+ "action.select-none": "Velg ingen",
+ "action.undo": "Angre",
+ "action.ungroup": "Avgrupper",
+ "action.zoom-in": "Zoom inn",
+ "action.zoom-out": "Zoom ut",
+ "action.zoom-to-fit": "Zoom for å passe",
+ "action.zoom-to-selection": "Zoom til valg",
+ "dash-style.draw": "Tegn",
+ "font-style.draw": "Tegn",
+ "geo-style.ellipse": "Ellipse",
+ "geo-style.rectangle": "Rektangel",
+ "geo-style.triangle": "Trekant",
+ "arrowheadStart-style.arrow": "Pil",
+ "arrowheadStart-style.triangle": "Trekant",
+ "arrowheadEnd-style.arrow": "Pil",
+ "arrowheadEnd-style.triangle": "Trekant",
+ "spline-style.line": "Linje",
+ "tool.select": "Velg",
+ "tool.draw": "Tegn",
+ "tool.eraser": "Viskelær",
+ "tool.arrow": "Pil",
+ "tool.ellipse": "Ellipse",
+ "tool.line": "Linje",
+ "tool.rectangle": "Rektangel",
+ "tool.triangle": "Trekant",
+ "tool.note": "Lapp",
+ "tool.text": "Tekst",
+ "menu.copy-as": "Kopier som",
+ "menu.edit": "Rediger",
+ "menu.export-as": "Eksporter som",
+ "menu.file": "Fil",
+ "menu.language": "Språk",
+ "menu.preferences": "Preferanser",
+ "menu.view": "Vis",
+ "context-menu.copy-as": "Kopier som",
+ "context-menu.export-as": "Eksporter som",
+ "context-menu.move-to-page": "Flytt til side",
+ "page-menu.submenu.delete": "Slett",
+ "shortcuts-dialog.edit": "Rediger",
+ "shortcuts-dialog.file": "Fil",
+ "shortcuts-dialog.preferences": "Preferanser",
+ "shortcuts-dialog.view": "Vis",
+ "style-panel.title": "Stiler",
+ "style-panel.align": "Juster",
+ "style-panel.color": "Farge",
+ "style-panel.dash": "Linje",
+ "style-panel.fill": "Fyll",
+ "style-panel.font": "Teksttype",
+ "style-panel.size": "Størrelse"
+}
diff --git a/apps/web/public/translations/pl.json b/apps/web/public/translations/pl.json
new file mode 100644
index 00000000..ab1b5046
--- /dev/null
+++ b/apps/web/public/translations/pl.json
@@ -0,0 +1,113 @@
+{
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-png": "Kopiuj jako PNG",
+ "action.copy-as-svg.short": "SVG",
+ "action.copy": "Kopiuj",
+ "action.cut": "Wytnij",
+ "action.delete": "Usuń",
+ "action.duplicate": "Powiel",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg.short": "SVG",
+ "action.flip-horizontal": "Odwróć w poziomie",
+ "action.flip-vertical": "Odwróć w pionie",
+ "action.group": "Grupuj",
+ "action.insert-media": "Załaduj multimedia",
+ "action.paste": "Wklej",
+ "action.redo": "Powtórz",
+ "action.select-all": "Zaznacz wszystko",
+ "action.select-none": "Odznacz wszystko",
+ "action.toggle-dark-mode.menu": "Tryb ciemny",
+ "action.undo": "Cofnij",
+ "action.ungroup": "Rozgrupuj",
+ "action.zoom-in": "Przybliż",
+ "action.zoom-out": "Oddal",
+ "action.zoom-to-fit": "Wypełnij ekran",
+ "action.zoom-to-selection": "Przybliż do zaznaczenia",
+ "color-style.black": "Czarny",
+ "color-style.blue": "Niebieski",
+ "color-style.green": "Zielony",
+ "color-style.grey": "Szary",
+ "color-style.light-blue": "Jasnoniebieski",
+ "color-style.light-green": "Jasnozielony",
+ "color-style.light-red": "Jasnoczerwony",
+ "color-style.light-violet": "Jasny fiolet",
+ "color-style.orange": "Pomarańczowy",
+ "color-style.red": "Czerwony",
+ "color-style.violet": "Fioletowy",
+ "color-style.yellow": "Żółty",
+ "dash-style.draw": "Rysuj",
+ "opacity-style.0.1": "10%",
+ "opacity-style.0.25": "25%",
+ "opacity-style.0.5": "50%",
+ "opacity-style.0.75": "75%",
+ "opacity-style.1": "100%",
+ "font-style.draw": "Rysuj",
+ "geo-style.diamond": "Romb",
+ "geo-style.ellipse": "Elipsa",
+ "geo-style.rectangle": "Prostokąt",
+ "geo-style.rhombus-2": "Romb 2",
+ "geo-style.rhombus": "Romb",
+ "geo-style.star": "Gwiazda",
+ "geo-style.trapezoid": "Trapez",
+ "geo-style.triangle": "Trójkąt",
+ "arrowheadStart-style.arrow": "Strzałka",
+ "arrowheadStart-style.diamond": "Romb",
+ "arrowheadStart-style.triangle": "Trójkąt",
+ "arrowheadEnd-style.arrow": "Strzałka",
+ "arrowheadEnd-style.diamond": "Romb",
+ "arrowheadEnd-style.square": "Kwadrat",
+ "arrowheadEnd-style.triangle": "Trójkąt",
+ "spline-style.line": "Linia",
+ "tool.select": "Zaznacz",
+ "tool.draw": "Rysuj",
+ "tool.eraser": "Gumka",
+ "tool.arrow": "Strzałka",
+ "tool.diamond": "Romb",
+ "tool.ellipse": "Elipsa",
+ "tool.hexagon": "Sześciokąt",
+ "tool.line": "Linia",
+ "tool.octagon": "Ośmiokąt",
+ "tool.oval": "Owal",
+ "tool.pentagon": "Pięciokąt",
+ "tool.rectangle": "Prostokąt",
+ "tool.rhombus": "Romb",
+ "tool.star": "Gwiazda",
+ "tool.triangle": "Trójkąt",
+ "tool.note": "Naklejka",
+ "tool.text": "Tekst",
+ "menu.copy-as": "Kopiuj jako",
+ "menu.edit": "Edycja",
+ "menu.export-as": "Eksportuj jako",
+ "menu.file": "Plik",
+ "menu.language": "Język",
+ "menu.preferences": "Preferencje",
+ "menu.view": "Widok",
+ "context-menu.copy-as": "Kopiuj jako",
+ "context-menu.export-as": "Eksportuj jako",
+ "context-menu.move-to-page": "Przenieś na stronę",
+ "page-menu.new-page-initial-name": "Strona 1",
+ "page-menu.submenu.delete": "Usuń",
+ "people-menu.title": "Ludzie",
+ "people-menu.change-color": "Zmień kolor",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.twitter": "Twitter",
+ "shortcuts-dialog.title": "Skróty klawiaturowe",
+ "shortcuts-dialog.edit": "Edycja",
+ "shortcuts-dialog.file": "Plik",
+ "shortcuts-dialog.preferences": "Preferencje",
+ "shortcuts-dialog.view": "Widok",
+ "style-panel.title": "Style",
+ "style-panel.align": "Wyrównanie",
+ "style-panel.color": "Kolor",
+ "style-panel.dash": "Linia",
+ "style-panel.fill": "Wypełnienie",
+ "style-panel.font": "Czcionka",
+ "style-panel.size": "Rozmiar",
+ "file-system.confirm-open.dont-show-again": "Nie pytaj ponownie",
+ "vscode.file-open.dont-show-again": "Nie pytaj ponownie",
+ "context.pages.new-page": "Nowa strona",
+ "style-panel.position": "Pozycja"
+}
diff --git a/apps/web/public/translations/pt-br.json b/apps/web/public/translations/pt-br.json
new file mode 100644
index 00000000..b4eb32d2
--- /dev/null
+++ b/apps/web/public/translations/pt-br.json
@@ -0,0 +1,333 @@
+{
+ "action.convert-to-bookmark": "Converter em Favorito",
+ "action.convert-to-embed": "Converter em Incorporar",
+ "action.open-embed-link": "Abrir link",
+ "action.align-bottom": "Alinhar embaixo",
+ "action.align-center-horizontal": "Alinhar ao centro na horizontal",
+ "action.align-center-vertical": "Alinhar ao centro na vertical",
+ "action.align-center-horizontal.short": "Alinhar na horizontal",
+ "action.align-center-vertical.short": "Alinhar na vertical",
+ "action.align-left": "Alinhar à esquerda",
+ "action.align-right": "Alinhar à direita",
+ "action.align-top": "Alinhar na parte superior",
+ "action.back-to-content": "Voltar ao conteúdo",
+ "action.bring-forward": "Trazer para frente",
+ "action.bring-to-front": "Trazer para a frente",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-json": "Copiar como JSON",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-png": "Copiar como PNG",
+ "action.copy-as-svg.short": "SVG",
+ "action.copy-as-svg": "Copiar como SVG",
+ "action.copy": "Copiar",
+ "action.cut": "Recortar",
+ "action.delete": "Excluir",
+ "action.unlock-all": "Destravar tudo",
+ "action.distribute-horizontal": "Distribuir horizontalmente",
+ "action.distribute-vertical": "Distribuir verticalmente",
+ "action.distribute-horizontal.short": "Distribuir H",
+ "action.distribute-vertical.short": "Distribuir V",
+ "action.duplicate": "Duplicar",
+ "action.edit-link": "Editar link",
+ "action.exit-pen-mode": "Sair do modo de caneta",
+ "action.export-as-json": "Exportar como JSON",
+ "action.export-as-png": "Exportar como PNG",
+ "action.export-as-svg": "Exportar como SVG",
+ "action.flip-horizontal": "Inverter horizontalmente",
+ "action.flip-vertical": "Inverter verticalmente",
+ "action.flip-horizontal.short": "Inverter H",
+ "action.flip-vertical.short": "Inverter V",
+ "action.fork-project": "Fazer uma cópia deste projeto",
+ "action.group": "Agrupar",
+ "action.insert-embed": "Inserir conteúdo incorporado",
+ "action.insert-media": "Enviar mídia",
+ "action.leave-shared-project": "Sair do projeto compartilhado",
+ "action.new-project": "Novo projeto",
+ "action.new-shared-project": "Novo projeto compartilhado",
+ "action.open-cursor-chat": "Chat de cursor",
+ "action.open-file": "Abrir arquivo",
+ "action.pack": "Compactar",
+ "action.paste": "Colar",
+ "action.print": "Imprimir",
+ "action.redo": "Refazer",
+ "action.rotate-ccw": "Girar no sentido anti-horário",
+ "action.rotate-cw": "Girar no sentido horário",
+ "action.save-copy": "Salvar uma cópia",
+ "action.select-all": "Selecionar tudo",
+ "action.select-none": "Desselecionar tudo",
+ "action.send-backward": "Enviar para trás",
+ "action.send-to-back": "Enviar para o fundo",
+ "action.share-project": "Compartilhar este projeto",
+ "action.stack-horizontal": "Empilhar horizontalmente",
+ "action.stack-vertical": "Empilhar verticalmente",
+ "action.stack-horizontal.short": "Empilhar H",
+ "action.stack-vertical.short": "Empilhar V",
+ "action.stop-following": "Parar de seguir",
+ "action.stretch-horizontal": "Esticar horizontalmente",
+ "action.stretch-vertical": "Esticar verticalmente",
+ "action.stretch-horizontal.short": "Esticar H",
+ "action.stretch-vertical.short": "Esticar V",
+ "action.toggle-auto-size": "Alternar tamanho automático",
+ "action.toggle-dark-mode.menu": "Modo escuro",
+ "action.toggle-dark-mode": "Alternar modo escuro",
+ "action.toggle-reduce-motion.menu": "Reduzir movimento",
+ "action.toggle-reduce-motion": "Alternar redução de movimento",
+ "action.toggle-debug-mode.menu": "Modo de depuração",
+ "action.toggle-debug-mode": "Alternar modo de depuração",
+ "action.toggle-focus-mode.menu": "Modo de foco",
+ "action.toggle-focus-mode": "Alternar modo de foco",
+ "action.toggle-grid.menu": "Mostrar grade",
+ "action.toggle-grid": "Alternar grade",
+ "action.toggle-lock": "Alternar bloqueio",
+ "action.toggle-snap-mode.menu": "Sempre ajustar",
+ "action.toggle-snap-mode": "Alternar sempre ajustar",
+ "action.toggle-tool-lock.menu": "Bloqueio de ferramenta",
+ "action.toggle-tool-lock": "Alternar bloqueio de ferramenta",
+ "action.toggle-transparent.context-menu": "Transparente",
+ "action.toggle-transparent.menu": "Transparente",
+ "action.toggle-transparent": "Alternar fundo transparente",
+ "action.undo": "Desfazer",
+ "action.ungroup": "Desagrupar",
+ "action.zoom-in": "Aumentar zoom",
+ "action.zoom-out": "Diminuir zoom",
+ "action.zoom-to-100": "Zoom para 100%",
+ "action.zoom-to-fit": "Zoom para ajustar",
+ "action.zoom-to-selection": "Zoom para seleção",
+ "color-style.black": "Preto",
+ "color-style.blue": "Azul",
+ "color-style.green": "Verde",
+ "color-style.grey": "Cinza",
+ "color-style.light-blue": "Azul claro",
+ "color-style.light-green": "Verde claro",
+ "color-style.light-red": "Vermelho claro",
+ "color-style.light-violet": "Violeta claro",
+ "color-style.orange": "Laranja",
+ "color-style.red": "Vermelho",
+ "color-style.violet": "Violeta",
+ "color-style.yellow": "Amarelo",
+ "fill-style.none": "Nenhum",
+ "fill-style.semi": "Semi",
+ "fill-style.solid": "Sólido",
+ "fill-style.pattern": "Padrão",
+ "dash-style.dashed": "Tracejado",
+ "dash-style.dotted": "Pontilhado",
+ "dash-style.draw": "Desenhar",
+ "dash-style.solid": "Sólido",
+ "size-style.s": "Pequeno",
+ "size-style.m": "Médio",
+ "size-style.l": "Grande",
+ "size-style.xl": "Extra grande",
+ "font-style.draw": "Desenhar",
+ "font-style.sans": "Sem serifa",
+ "font-style.serif": "Com serifa",
+ "font-style.mono": "Monoespaçado",
+ "align-style.start": "Início",
+ "align-style.middle": "Meio",
+ "align-style.end": "Fim",
+ "align-style.justify": "Justificar",
+ "geo-style.arrow-down": "Seta para baixo",
+ "geo-style.arrow-left": "Seta para a esquerda",
+ "geo-style.arrow-right": "Seta para a direita",
+ "geo-style.arrow-up": "Seta para cima",
+ "geo-style.diamond": "Diamante",
+ "geo-style.ellipse": "Elipse",
+ "geo-style.hexagon": "Hexágono",
+ "geo-style.octagon": "Octógono",
+ "geo-style.oval": "Oval",
+ "geo-style.cloud": "Nuvem",
+ "geo-style.pentagon": "Pentágono",
+ "geo-style.rectangle": "Retângulo",
+ "geo-style.rhombus-2": "Rombos 2",
+ "geo-style.rhombus": "Rombos",
+ "geo-style.star": "Estrela",
+ "geo-style.trapezoid": "Trapezoide",
+ "geo-style.triangle": "Triângulo",
+ "geo-style.x-box": "Caixa com X",
+ "geo-style.check-box": "Caixa de seleção",
+ "arrowheadStart-style.none": "Nenhum",
+ "arrowheadStart-style.arrow": "Seta",
+ "arrowheadStart-style.bar": "Barra",
+ "arrowheadStart-style.diamond": "Diamante",
+ "arrowheadStart-style.dot": "Ponto",
+ "arrowheadStart-style.inverted": "Invertido",
+ "arrowheadStart-style.pipe": "Tubo",
+ "arrowheadStart-style.square": "Quadrado",
+ "arrowheadStart-style.triangle": "Triângulo",
+ "arrowheadEnd-style.none": "Nenhum",
+ "arrowheadEnd-style.arrow": "Seta",
+ "arrowheadEnd-style.bar": "Barra",
+ "arrowheadEnd-style.diamond": "Diamante",
+ "arrowheadEnd-style.dot": "Ponto",
+ "arrowheadEnd-style.inverted": "Invertido",
+ "arrowheadEnd-style.pipe": "Tubo",
+ "arrowheadEnd-style.square": "Quadrado",
+ "arrowheadEnd-style.triangle": "Triângulo",
+ "spline-style.line": "Linha",
+ "spline-style.cubic": "Cúbica",
+ "tool.select": "Selecionar",
+ "tool.hand": "Mão",
+ "tool.draw": "Desenhar",
+ "tool.eraser": "Borracha",
+ "tool.arrow-down": "Seta para baixo",
+ "tool.arrow-left": "Seta para a esquerda",
+ "tool.arrow-right": "Seta para a direita",
+ "tool.arrow-up": "Seta para cima",
+ "tool.arrow": "Seta",
+ "tool.cloud": "Nuvem",
+ "tool.diamond": "Diamante",
+ "tool.ellipse": "Elipse",
+ "tool.hexagon": "Hexágono",
+ "tool.highlight": "Destaque",
+ "tool.line": "Linha",
+ "tool.octagon": "Octógono",
+ "tool.oval": "Oval",
+ "tool.pentagon": "Pentágono",
+ "tool.rectangle": "Retângulo",
+ "tool.rhombus": "Rombos",
+ "tool.star": "Estrela",
+ "tool.trapezoid": "Trapezoide",
+ "tool.triangle": "Triângulo",
+ "tool.x-box": "Caixa com X",
+ "tool.check-box": "Caixa de seleção",
+ "tool.asset": "Ativo",
+ "tool.frame": "Moldura",
+ "tool.note": "Anotação",
+ "tool.laser": "Laser",
+ "tool.embed": "Incorporar",
+ "tool.text": "Texto",
+ "menu.title": "Menu",
+ "menu.copy-as": "Copiar como",
+ "menu.edit": "Editar",
+ "menu.export-as": "Exportar como",
+ "menu.file": "Arquivo",
+ "menu.language": "Idioma",
+ "menu.preferences": "Preferências",
+ "menu.view": "Visualizar",
+ "context-menu.arrange": "Organizar",
+ "context-menu.copy-as": "Copiar como",
+ "context-menu.export-as": "Exportar como",
+ "context-menu.move-to-page": "Mover para a página",
+ "context-menu.reorder": "Reordenar",
+ "page-menu.title": "Páginas",
+ "page-menu.create-new-page": "Criar nova página",
+ "page-menu.max-page-count-reached": "Limite de páginas atingido",
+ "page-menu.edit-start": "Editar",
+ "page-menu.edit-done": "Concluído",
+ "page-menu.go-to-page": "Ir para a página",
+ "page-menu.submenu.rename": "Renomear",
+ "page-menu.submenu.duplicate-page": "Duplicar",
+ "page-menu.submenu.title": "Menu",
+ "page-menu.submenu.move-down": "Mover para baixo",
+ "page-menu.submenu.move-up": "Mover para cima",
+ "page-menu.submenu.delete": "Excluir",
+ "share-menu.title": "Compartilhar",
+ "share-menu.save-note": "Baixe este projeto para o seu computador como um arquivo .tldr.",
+ "share-menu.fork-note": "Crie um novo projeto compartilhado com base nesta captura de tela.",
+ "share-menu.share-project": "Compartilhar este projeto",
+ "share-menu.default-project-name": "Projeto Compartilhado",
+ "share-menu.copy-link": "Copiar link de compartilhamento",
+ "share-menu.readonly-link": "Somente leitura",
+ "share-menu.create-snapshot-link": "Copiar link da captura de tela",
+ "share-menu.snapshot-link-note": "Capture e compartilhe este projeto como um link de captura de tela somente leitura.",
+ "share-menu.copy-readonly-link": "Copiar link de somente leitura",
+ "share-menu.offline-note": "Crie um novo projeto compartilhado com base no seu projeto atual.",
+ "share-menu.copy-link-note": "Qualquer pessoa com o link poderá visualizar e editar este projeto.",
+ "share-menu.copy-readonly-link-note": "Qualquer pessoa com o link poderá visualizar (mas não editar) este projeto.",
+ "share-menu.project-too-large": "Desculpe, este projeto não pode ser compartilhado porque é muito grande. Estamos trabalhando nisso!",
+ "share-menu.upload-failed": "Desculpe, não conseguimos fazer o upload do seu projeto no momento. Tente novamente ou informe-nos se o problema persistir.",
+ "people-menu.title": "Pessoas",
+ "people-menu.change-name": "Alterar nome",
+ "people-menu.change-color": "Alterar cor",
+ "people-menu.follow": "Seguindo",
+ "people-menu.following": "Seguindo",
+ "people-menu.leading": "Seguindo você",
+ "people-menu.user": "(Você)",
+ "people-menu.invite": "Convidar outros",
+ "help-menu.title": "Ajuda e recursos",
+ "help-menu.about": "Sobre",
+ "help-menu.keyboard-shortcuts": "Atalhos de teclado",
+ "actions-menu.title": "Ações",
+ "edit-link-dialog.title": "Editar link",
+ "edit-link-dialog.invalid-url": "Um link deve ser uma URL válida.",
+ "edit-link-dialog.detail": "Os links serão abertos em uma nova aba.",
+ "edit-link-dialog.clear": "Limpar",
+ "edit-link-dialog.save": "Continuar",
+ "edit-link-dialog.cancel": "Cancelar",
+ "embed-dialog.title": "Inserir conteúdo incorporado",
+ "embed-dialog.back": "Voltar",
+ "embed-dialog.create": "Criar",
+ "embed-dialog.cancel": "Cancelar",
+ "embed-dialog.url": "URL",
+ "embed-dialog.instruction": "Cole a URL do site para criar o conteúdo incorporado.",
+ "embed-dialog.invalid-url": "Não foi possível criar um conteúdo incorporado a partir dessa URL.",
+ "edit-pages-dialog.move-down": "Mover para baixo",
+ "edit-pages-dialog.move-up": "Mover para cima",
+ "shortcuts-dialog.title": "Atalhos de teclado",
+ "shortcuts-dialog.edit": "Editar",
+ "shortcuts-dialog.file": "Arquivo",
+ "shortcuts-dialog.preferences": "Preferências",
+ "shortcuts-dialog.tools": "Ferramentas",
+ "shortcuts-dialog.transform": "Transformar",
+ "shortcuts-dialog.view": "Visualizar",
+ "shortcuts-dialog.collaboration": "Colaboração",
+ "home-project-dialog.title": "Projeto inicial",
+ "home-project-dialog.description": "Este é o seu projeto inicial local. É apenas para você!",
+ "rename-project-dialog.title": "Renomear projeto",
+ "rename-project-dialog.cancel": "Cancelar",
+ "rename-project-dialog.rename": "Renomear",
+ "style-panel.title": "Estilos",
+ "style-panel.align": "Alinhar",
+ "style-panel.vertical-align": "Alinhamento vertical",
+ "style-panel.position": "Posição",
+ "style-panel.arrowheads": "Setas",
+ "style-panel.arrowhead-start": "Início",
+ "style-panel.arrowhead-end": "Fim",
+ "style-panel.color": "Cor",
+ "style-panel.dash": "Tracejado",
+ "style-panel.fill": "Preenchimento",
+ "style-panel.font": "Fonte",
+ "style-panel.geo": "Forma",
+ "style-panel.mixed": "Misto",
+ "style-panel.opacity": "Opacidade",
+ "style-panel.size": "Tamanho",
+ "style-panel.spline": "Spline",
+ "tool-panel.drawing": "Desenho",
+ "tool-panel.shapes": "Formas",
+ "tool-panel.more": "Mais",
+ "debug-panel.more": "Mais",
+ "navigation-zone.toggle-minimap": "Alternar minimapa",
+ "focus-mode.toggle-focus-mode": "Alternar modo de foco",
+ "toast.close": "Fechar",
+ "file-system.file-open-error.title": "Não foi possível abrir o arquivo",
+ "file-system.file-open-error.not-a-tldraw-file": "O arquivo que você tentou abrir não parece ser um arquivo tldraw.",
+ "file-system.file-open-error.file-format-version-too-new": "O arquivo que você tentou abrir é de uma versão mais recente do tldraw. Por favor, recarregue a página e tente novamente.",
+ "file-system.file-open-error.generic-corrupted-file": "O arquivo que você tentou abrir está corrompido.",
+ "file-system.confirm-open.title": "Sobrescrever projeto atual?",
+ "file-system.confirm-open.description": "Abrir um arquivo substituirá seu projeto atual e quaisquer alterações não salvas serão perdidas. Você tem certeza de que deseja continuar?",
+ "file-system.confirm-open.cancel": "Cancelar",
+ "file-system.confirm-open.open": "Abrir arquivo",
+ "file-system.confirm-open.dont-show-again": "Não perguntar novamente",
+ "file-system.confirm-clear.title": "Limpar projeto atual?",
+ "file-system.confirm-clear.description": "Criar um novo projeto apagará seu projeto atual e quaisquer alterações não salvas serão perdidas. Você tem certeza de que deseja continuar?",
+ "file-system.confirm-clear.cancel": "Cancelar",
+ "file-system.confirm-clear.continue": "Continuar",
+ "file-system.confirm-clear.dont-show-again": "Não perguntar novamente",
+ "file-system.shared-document-file-open-error.title": "Não foi possível abrir o arquivo",
+ "file-system.shared-document-file-open-error.description": "A abertura de arquivos de projetos compartilhados não é suportada.",
+ "sharing.confirm-leave.title": "Sair do projeto atual?",
+ "sharing.confirm-leave.description": "Você tem certeza de que deseja sair deste projeto compartilhado? Você pode retornar a ele navegando até sua URL.",
+ "sharing.confirm-leave.cancel": "Cancelar",
+ "sharing.confirm-leave.leave": "Sair",
+ "sharing.confirm-leave.dont-show-again": "Não perguntar novamente",
+ "toast.error.export-fail.title": "Falha na exportação",
+ "toast.error.export-fail.desc": "Falha ao exportar a imagem",
+ "toast.error.copy-fail.title": "Falha na cópia",
+ "toast.error.copy-fail.desc": "Falha ao copiar a imagem",
+ "context.pages.new-page": "Nova página",
+ "vscode.file-open.desc": "Atualizamos este documento para funcionar com a versão atual do tldraw. Se você deseja manter a versão original (que funcionará em old.tldraw.com), clique abaixo para criar um backup.",
+ "vscode.file-open.open": "Continuar",
+ "vscode.file-open.backup-saved": "Backup salvo",
+ "vscode.file-open.backup-failed": "Falha no backup: este não é um arquivo .tldr.",
+ "vscode.file-open.dont-show-again": "Não perguntar novamente",
+ "cursor-chat.type-to-chat": "Digite para conversar..."
+}
diff --git a/apps/web/public/translations/pt-pt.json b/apps/web/public/translations/pt-pt.json
new file mode 100644
index 00000000..0bd59aa6
--- /dev/null
+++ b/apps/web/public/translations/pt-pt.json
@@ -0,0 +1,86 @@
+{
+ "action.bring-forward": "Mover acima",
+ "action.bring-to-front": "Colocar à Frente",
+ "action.copy": "Copiar",
+ "action.cut": "Cortar",
+ "action.delete": "Apagar",
+ "action.duplicate": "Duplicar",
+ "action.flip-horizontal": "Inverter Horizontalmente",
+ "action.flip-vertical": "Inverter Verticalmente",
+ "action.flip-horizontal.short": "Inverter Horizontalmente",
+ "action.flip-vertical.short": "Inverter Verticalmente",
+ "action.group": "Agrupar",
+ "action.insert-media": "Upload Média",
+ "action.paste": "Colar",
+ "action.redo": "Refazer",
+ "action.select-all": "Selecionar todos",
+ "action.select-none": "Selecionar nenhum",
+ "action.send-backward": "Mover abaixo",
+ "action.send-to-back": "Colocar no Fundo",
+ "action.toggle-dark-mode.menu": "Modo Escuro",
+ "action.toggle-dark-mode": "Modo Escuro",
+ "action.toggle-debug-mode.menu": "Modo Debug",
+ "action.toggle-debug-mode": "Modo Debug",
+ "action.toggle-focus-mode.menu": "Modo Foco",
+ "action.toggle-focus-mode": "Modo Foco",
+ "action.toggle-grid.menu": "Mostrar Grelha",
+ "action.toggle-grid": "Mostrar Grelha",
+ "action.toggle-snap-mode.menu": "Mostrar Pontos de Ajuste",
+ "action.toggle-snap-mode": "Mostrar Pontos de Ajuste",
+ "action.undo": "Desfazer",
+ "action.ungroup": "Desagrupar",
+ "action.zoom-in": "Aumentar zoom",
+ "action.zoom-out": "Diminuir zoom",
+ "action.zoom-to-fit": "Zoom para caber",
+ "action.zoom-to-selection": "Zoom na seleção",
+ "dash-style.draw": "Desenhar",
+ "font-style.draw": "Desenhar",
+ "geo-style.ellipse": "Elipse",
+ "geo-style.rectangle": "Retângulo",
+ "geo-style.triangle": "Triângulo",
+ "arrowheadStart-style.arrow": "Seta",
+ "arrowheadStart-style.triangle": "Triângulo",
+ "arrowheadEnd-style.arrow": "Seta",
+ "arrowheadEnd-style.triangle": "Triângulo",
+ "spline-style.line": "Linha",
+ "tool.select": "Selecionar",
+ "tool.draw": "Desenhar",
+ "tool.eraser": "Borracha",
+ "tool.arrow": "Seta",
+ "tool.ellipse": "Elipse",
+ "tool.line": "Linha",
+ "tool.rectangle": "Retângulo",
+ "tool.triangle": "Triângulo",
+ "tool.note": "Post-it",
+ "tool.text": "Texto",
+ "menu.copy-as": "Copiar como",
+ "menu.edit": "Editar",
+ "menu.export-as": "Exportar como",
+ "menu.file": "Ficheiro",
+ "menu.language": "Língua",
+ "menu.preferences": "Preferências",
+ "menu.view": "Visualizar",
+ "context-menu.copy-as": "Copiar como",
+ "context-menu.export-as": "Exportar como",
+ "context-menu.move-to-page": "Mover para Página",
+ "page-menu.create-new-page": "Criar Página",
+ "page-menu.edit-start": "Editar",
+ "page-menu.submenu.duplicate-page": "Duplicar",
+ "page-menu.submenu.delete": "Apagar",
+ "share-menu.copy-link": "Copiar Link de Convite",
+ "edit-link-dialog.cancel": "Cancelar",
+ "embed-dialog.cancel": "Cancelar",
+ "shortcuts-dialog.edit": "Editar",
+ "shortcuts-dialog.file": "Ficheiro",
+ "shortcuts-dialog.preferences": "Preferências",
+ "shortcuts-dialog.view": "Visualizar",
+ "style-panel.title": "Estilos",
+ "style-panel.align": "Alinhamento",
+ "style-panel.color": "Cor",
+ "style-panel.dash": "Traço",
+ "style-panel.fill": "Preencher",
+ "style-panel.font": "Fonte",
+ "style-panel.size": "Tamanho",
+ "focus-mode.toggle-focus-mode": "Modo Foco",
+ "file-system.confirm-open.cancel": "Cancelar"
+}
diff --git a/apps/web/public/translations/ro.json b/apps/web/public/translations/ro.json
new file mode 100644
index 00000000..badb697a
--- /dev/null
+++ b/apps/web/public/translations/ro.json
@@ -0,0 +1,371 @@
+{
+ "action.align-bottom": "Aliniere jos",
+ "action.align-center-horizontal": "Aliniere orizontală",
+ "action.align-center-horizontal.short": "Aliniere orizontală",
+ "action.align-center-vertical": "Aliniere verticală",
+ "action.align-center-vertical.short": "Aliniere verticală",
+ "action.align-left": "Aliniere la stânga",
+ "action.align-right": "Aliniere la dreapta",
+ "action.align-top": "Aliniere sus",
+ "action.back-to-content": "Înapoi la conținut",
+ "action.bring-forward": "Adu în față",
+ "action.bring-to-front": "Adu în prim plan",
+ "action.convert-to-bookmark": "Convertește la marcaj",
+ "action.convert-to-embed": "Convertește la înglobare",
+ "action.copy": "Copiază",
+ "action.copy-as-json": "Copiază ca JSON",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "Copiază ca PNG",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "Copiază ca SVG",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "Decupează",
+ "action.delete": "Șterge",
+ "action.distribute-horizontal": "Distribuit orizontal",
+ "action.distribute-horizontal.short": "Distribuit orizontal",
+ "action.distribute-vertical": "Distribuit vertical",
+ "action.distribute-vertical.short": "Distribuit vertical",
+ "action.duplicate": "Fă duplicat",
+ "action.edit-link": "Editează legătură",
+ "action.exit-pen-mode": "Ieși din modul stilou",
+ "action.export-all-as-json": "Exportă totul ca JSON",
+ "action.export-all-as-json.short": "JSON",
+ "action.export-all-as-png": "Exportă totul ca PNG",
+ "action.export-all-as-png.short": "PNG",
+ "action.export-all-as-svg": "Exportă totul ca SVG",
+ "action.export-all-as-svg.short": "SVG",
+ "action.export-as-json": "Exportă ca JSON",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "Exportă ca PNG",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "Exportă ca SVG",
+ "action.export-as-svg.short": "SVG",
+ "action.fit-frame-to-content": "Potrivește la conținut",
+ "action.flip-horizontal": "Întoarce pe orizontală",
+ "action.flip-horizontal.short": "Întoarce pe orizontală",
+ "action.flip-vertical": "Întoarce pe verticală",
+ "action.flip-vertical.short": "Întoarce pe verticală",
+ "action.fork-project": "Ramifică acest proiect",
+ "action.group": "Grupare",
+ "action.insert-embed": "Inserează încorporare",
+ "action.insert-media": "Încarcă media",
+ "action.leave-shared-project": "Părăsește proiectul partajat",
+ "action.new-project": "Proiect nou",
+ "action.new-shared-project": "Proiect partajat nou",
+ "action.open-cursor-chat": "Cursor discuție",
+ "action.open-embed-link": "Deschide legătură",
+ "action.open-file": "Deschide fișier",
+ "action.pack": "Pachet",
+ "action.paste": "Lipește",
+ "action.print": "Imprimă",
+ "action.redo": "Refă",
+ "action.remove-frame": "Elimină ramă",
+ "action.rename": "Redenumește",
+ "action.rotate-ccw": "Rotire în sens invers acelor de ceasornic",
+ "action.rotate-cw": "Rotire în sensul acelor de ceasornic",
+ "action.save-copy": "Salvează o copie",
+ "action.select-all": "Selectează tot",
+ "action.select-none": "Nu selecta nimic",
+ "action.send-backward": "Trimite în spate",
+ "action.send-to-back": "Trimite înapoi",
+ "action.share-project": "Partajează acest proiect",
+ "action.stack-horizontal": "Stivă orizontal",
+ "action.stack-horizontal.short": "Stivă orizontal",
+ "action.stack-vertical": "Stivă vertical",
+ "action.stack-vertical.short": "Stivă verticală",
+ "action.stop-following": "Oprește urmărirea",
+ "action.stretch-horizontal": "Întinde orizontal",
+ "action.stretch-horizontal.short": "Întinde orizontal",
+ "action.stretch-vertical": "Întinde vertical",
+ "action.stretch-vertical.short": "Întinde vertical",
+ "action.toggle-auto-size": "Comută dimensiune automată",
+ "action.toggle-dark-mode": "Comută mod întunecat",
+ "action.toggle-dark-mode.menu": "Mod întunecat",
+ "action.toggle-debug-mode": "Comută mod depanare",
+ "action.toggle-debug-mode.menu": "Mod depanare",
+ "action.toggle-edge-scrolling": "Comută derularea marginilor",
+ "action.toggle-edge-scrolling.menu": "Derulare margini",
+ "action.toggle-focus-mode": "Comută mod focalizare",
+ "action.toggle-focus-mode.menu": "Mod focalizare",
+ "action.toggle-grid": "Comută grilă",
+ "action.toggle-grid.menu": "Arată grilă",
+ "action.toggle-lock": "Blochează/deblochează",
+ "action.toggle-reduce-motion": "Comută reducere mișcare",
+ "action.toggle-reduce-motion.menu": "Redu mișcare",
+ "action.toggle-snap-mode": "Comută ruptura întotdeauna",
+ "action.toggle-snap-mode.menu": "Rupe întotdeauna",
+ "action.toggle-tool-lock": "Comută blocarea uneltei",
+ "action.toggle-tool-lock.menu": "Blocare unealtă",
+ "action.toggle-transparent": "Comută fundal transparent",
+ "action.toggle-transparent.context-menu": "Transparent",
+ "action.toggle-transparent.menu": "Transparent",
+ "action.toggle-wrap-mode": "Comută selectare la împachetare",
+ "action.toggle-wrap-mode.menu": "Selectează la împachetare",
+ "action.undo": "Anulează",
+ "action.ungroup": "Anulează gruparea",
+ "action.unlock-all": "Deblochează tot",
+ "action.zoom-in": "Mărește",
+ "action.zoom-out": "Micșorează",
+ "action.zoom-to-100": "Mărește la 100%",
+ "action.zoom-to-fit": "Mărește pentru a se potrivi",
+ "action.zoom-to-selection": "Mărește la selecție",
+ "actions-menu.title": "Acțiuni",
+ "align-style.end": "Sfârșit",
+ "align-style.justify": "Justificat",
+ "align-style.middle": "Mijloc",
+ "align-style.start": "Începe",
+ "arrowheadEnd-style.arrow": "Săgeată",
+ "arrowheadEnd-style.bar": "Bară",
+ "arrowheadEnd-style.diamond": "Diamant",
+ "arrowheadEnd-style.dot": "Punct",
+ "arrowheadEnd-style.inverted": "Inversat",
+ "arrowheadEnd-style.none": "Niciunul",
+ "arrowheadEnd-style.pipe": "Conductă",
+ "arrowheadEnd-style.square": "Pătrat",
+ "arrowheadEnd-style.triangle": "Triunghi",
+ "arrowheadStart-style.arrow": "Săgeată",
+ "arrowheadStart-style.bar": "Bară",
+ "arrowheadStart-style.diamond": "Diamant",
+ "arrowheadStart-style.dot": "Punct",
+ "arrowheadStart-style.inverted": "Inversat",
+ "arrowheadStart-style.none": "Niciunul",
+ "arrowheadStart-style.pipe": "Conductă",
+ "arrowheadStart-style.square": "Pătrat",
+ "arrowheadStart-style.triangle": "Triunghi",
+ "assets.files.upload-failed": "Încărcare eșuată",
+ "assets.url.failed": "Nu s-a putut încărca previzualizarea URL-ului",
+ "color-style.black": "Negru",
+ "color-style.blue": "Albastru",
+ "color-style.green": "Verde",
+ "color-style.grey": "Gri",
+ "color-style.light-blue": "Albastru deschis",
+ "color-style.light-green": "Verde deschis",
+ "color-style.light-red": "Roșu deschis",
+ "color-style.light-violet": "Violet deschis",
+ "color-style.orange": "Portocaliu",
+ "color-style.red": "Roșu",
+ "color-style.violet": "Violet",
+ "color-style.yellow": "Galben",
+ "context-menu.arrange": "Aranjează",
+ "context-menu.copy-as": "Copiază ca",
+ "context-menu.export-all-as": "Exportă totul ca",
+ "context-menu.export-as": "Exportă ca",
+ "context-menu.move-to-page": "Mută în pagina",
+ "context-menu.reorder": "Reordonează",
+ "context.pages.new-page": "Pagină nouă",
+ "cursor-chat.type-to-chat": "Tastează pentru a discuta...",
+ "dash-style.dashed": "Întreruptă",
+ "dash-style.dotted": "Punctată",
+ "dash-style.draw": "Desenează",
+ "dash-style.solid": "Solid",
+ "debug-panel.more": "Mai multe",
+ "document.default-name": "Fără titlu",
+ "edit-link-dialog.cancel": "Anulează",
+ "edit-link-dialog.clear": "Golește",
+ "edit-link-dialog.detail": "Legăturile se vor deschide într-o filă nouă.",
+ "edit-link-dialog.invalid-url": "O legătură trebuie să fie un URL valid.",
+ "edit-link-dialog.save": "Continuă",
+ "edit-link-dialog.title": "Editează legătură",
+ "edit-link-dialog.url": "URL",
+ "edit-pages-dialog.move-down": "Mută jos",
+ "edit-pages-dialog.move-up": "Mută sus",
+ "embed-dialog.back": "Înapoi",
+ "embed-dialog.cancel": "Anulează",
+ "embed-dialog.create": "Creează",
+ "embed-dialog.instruction": "Introdu URL-ul site-ului pentru a crea înglobarea.",
+ "embed-dialog.invalid-url": "Nu am putut crea o încorporare din acea adresă URL.",
+ "embed-dialog.title": "Creează înglobare",
+ "embed-dialog.url": "URL",
+ "file-system.confirm-clear.cancel": "Anulează",
+ "file-system.confirm-clear.continue": "Continuă",
+ "file-system.confirm-clear.description": "Crearea unui nou proiect va șterge proiectul curent și orice modificări nesalvate se vor pierde. Sigur vrei să continui?",
+ "file-system.confirm-clear.dont-show-again": "Nu mai întreba",
+ "file-system.confirm-clear.title": "Golești proiectul curent?",
+ "file-system.confirm-open.cancel": "Anulează",
+ "file-system.confirm-open.description": "Deschiderea unui fișier va înlocui proiectul curent și orice modificări nesalvate se vor pierde. Sigur vrei să continui?",
+ "file-system.confirm-open.dont-show-again": "Nu mai întreba",
+ "file-system.confirm-open.open": "Deschide fișier",
+ "file-system.confirm-open.title": "Suprascrii proiectul actual?",
+ "file-system.file-open-error.file-format-version-too-new": "Fișierul pe care ai încercat să îl deschizi este dintr-o versiune mai nouă de tldraw. Te rog reîncarcă pagina și încearcă din nou.",
+ "file-system.file-open-error.generic-corrupted-file": "Fișierul pe care ai încercat să îl deschizi este corupt.",
+ "file-system.file-open-error.not-a-tldraw-file": "Fișierul pe care ai încercat să îl deschizi nu arată ca un fișier tldraw.",
+ "file-system.file-open-error.title": "Nu s-a putut deschide fișierul",
+ "file-system.shared-document-file-open-error.description": "Deschiderea fișierelor din proiectele partajate nu este acceptată.",
+ "file-system.shared-document-file-open-error.title": "Fișierul nu a putut fi deschis",
+ "fill-style.none": "Fără",
+ "fill-style.pattern": "Model",
+ "fill-style.semi": "Semi",
+ "fill-style.solid": "Solid",
+ "focus-mode.toggle-focus-mode": "Comută în modul focalizare",
+ "font-style.draw": "Desenează",
+ "font-style.mono": "Mono",
+ "font-style.sans": "Sans",
+ "font-style.serif": "Serif",
+ "geo-style.arrow-down": "Săgeata în jos",
+ "geo-style.arrow-left": "Săgeată la stânga",
+ "geo-style.arrow-right": "Săgeată la dreapta",
+ "geo-style.arrow-up": "Săgeată în sus",
+ "geo-style.check-box": "Bifă",
+ "geo-style.cloud": "Cloud",
+ "geo-style.diamond": "Diamant",
+ "geo-style.ellipse": "Elipsă",
+ "geo-style.hexagon": "Hexagon",
+ "geo-style.octagon": "Octogon",
+ "geo-style.oval": "Oval",
+ "geo-style.pentagon": "Pentagon",
+ "geo-style.rectangle": "Dreptunghi",
+ "geo-style.rhombus": "Romb",
+ "geo-style.rhombus-2": "Romb 2",
+ "geo-style.star": "Stea",
+ "geo-style.trapezoid": "Trapez",
+ "geo-style.triangle": "Triunghi",
+ "geo-style.x-box": "Casetă X",
+ "help-menu.about": "Despre",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "Scurtături tastatură",
+ "help-menu.title": "Ajutor și resurse",
+ "help-menu.twitter": "Twitter",
+ "home-project-dialog.description": "Acesta este proiectul tău local. Este doar pentru tine!",
+ "home-project-dialog.ok": "Ok",
+ "home-project-dialog.title": "Proiect de acasă",
+ "menu.copy-as": "Copiază ca",
+ "menu.edit": "Editează",
+ "menu.export-as": "Exportă ca",
+ "menu.file": "Fișier",
+ "menu.language": "Limbi",
+ "menu.preferences": "Preferințe",
+ "menu.title": "Meniu",
+ "menu.view": "Vezi",
+ "navigation-zone.toggle-minimap": "Comută mini-hartă",
+ "navigation-zone.zoom": "Mărește",
+ "opacity-style.0.1": "10%",
+ "opacity-style.0.25": "25%",
+ "opacity-style.0.5": "50%",
+ "opacity-style.0.75": "75%",
+ "opacity-style.1": "100%",
+ "page-menu.create-new-page": "Creează pagină nouă",
+ "page-menu.edit-done": "Gata",
+ "page-menu.edit-start": "Editează",
+ "page-menu.go-to-page": "Mergi la pagina",
+ "page-menu.max-page-count-reached": "Numărul maxim de pagini a fost atins",
+ "page-menu.new-page-initial-name": "Pagina 1",
+ "page-menu.submenu.delete": "Șterge",
+ "page-menu.submenu.duplicate-page": "Fă duplicat",
+ "page-menu.submenu.move-down": "Mută jos",
+ "page-menu.submenu.move-up": "Mută sus",
+ "page-menu.submenu.rename": "Redenumește",
+ "page-menu.submenu.title": "Meniu",
+ "page-menu.title": "Pagini",
+ "people-menu.change-color": "Schimbă culoare",
+ "people-menu.change-name": "Schimbă nume",
+ "people-menu.follow": "Urmărește",
+ "people-menu.following": "Urmărește",
+ "people-menu.invite": "Invită-i pe alții",
+ "people-menu.leading": "Te urmărește",
+ "people-menu.title": "Persoane",
+ "people-menu.user": "(Tu)",
+ "rename-project-dialog.cancel": "Anulează",
+ "rename-project-dialog.rename": "Redenumește",
+ "rename-project-dialog.title": "Redenumește proiect",
+ "share-menu.copy-link": "Copiază legătură",
+ "share-menu.copy-link-note": "Oricine are legătura va putea vizualiza și edita acest proiect.",
+ "share-menu.copy-readonly-link": "Copiază legătură doar citire",
+ "share-menu.copy-readonly-link-note": "Oricine are legătura va putea vizualiza (dar nu va putea edita) acest proiect.",
+ "share-menu.create-snapshot-link": "Creează o legătură instantanee",
+ "share-menu.default-project-name": "Proiect partajat",
+ "share-menu.fork-note": "Creează un nou proiect partajat pe baza acestui instantaneu.",
+ "share-menu.offline-note": "Partajarea acestui proiect va crea o copie live găzduită la o adresă URL nouă. Poți partaja adresa URL cu până la treizeci de alte persoane pentru a vizualiza și a edita proiectul împreună.",
+ "share-menu.project-too-large": "Scuze, acest proiect nu poate fi partajat deoarece este prea mare. Lucrăm la asta!",
+ "share-menu.readonly-link": "Doar citire",
+ "share-menu.save-note": "Descarcă acest proiect pe calculatorul tău ca un fișier .tldr.",
+ "share-menu.share-project": "Partajează acest proiect",
+ "share-menu.snapshot-link-note": "Capturează și partajează acest proiect ca o legătură instantanee numai pentru citire.",
+ "share-menu.title": "Partajează",
+ "share-menu.upload-failed": "Ne pare rău, momentan nu am putut încărca proiectul tău. Te rugăm să încerci din nou sau să ne anunți dacă problema persistă.",
+ "sharing.confirm-leave.cancel": "Anulează",
+ "sharing.confirm-leave.description": "Sigur vrei să părăsești acest proiect partajat? Te poți întoarce la el navigând la URL-ul său.",
+ "sharing.confirm-leave.dont-show-again": "Nu mai întreba",
+ "sharing.confirm-leave.leave": "Părăsește",
+ "sharing.confirm-leave.title": "Părăsești proiectul partajat?",
+ "shortcuts-dialog.collaboration": "Colaborare",
+ "shortcuts-dialog.edit": "Editare",
+ "shortcuts-dialog.file": "Fișier",
+ "shortcuts-dialog.preferences": "Preferințe",
+ "shortcuts-dialog.title": "Scurtături tastatură",
+ "shortcuts-dialog.tools": "Unelte",
+ "shortcuts-dialog.transform": "Transformare",
+ "shortcuts-dialog.view": "Vizualizare",
+ "size-style.l": "Mare",
+ "size-style.m": "Mediu",
+ "size-style.s": "Mic",
+ "size-style.xl": "Foarte mare",
+ "spline-style.cubic": "Cubic",
+ "spline-style.line": "Linie",
+ "status.offline": "Deconectat",
+ "status.online": "Conectat",
+ "style-panel.align": "Aliniere",
+ "style-panel.arrowhead-end": "Sfârșit",
+ "style-panel.arrowhead-start": "Începe",
+ "style-panel.arrowheads": "Vârfuri de săgeți",
+ "style-panel.color": "Culoare",
+ "style-panel.dash": "Liniuță",
+ "style-panel.fill": "Umplere",
+ "style-panel.font": "Font",
+ "style-panel.geo": "Formă",
+ "style-panel.mixed": "Amestecat",
+ "style-panel.opacity": "Opacitate",
+ "style-panel.position": "Poziție",
+ "style-panel.size": "Mărime",
+ "style-panel.spline": "Spline",
+ "style-panel.title": "Stiluri",
+ "style-panel.vertical-align": "Aliniere verticală",
+ "toast.close": "Închide",
+ "toast.error.copy-fail.desc": "Copierea imaginii a eșuat",
+ "toast.error.copy-fail.title": "Copiere eșuată",
+ "toast.error.export-fail.desc": "Exportarea imaginii a eșuat",
+ "toast.error.export-fail.title": "Export eșuat",
+ "tool-panel.drawing": "Desen",
+ "tool-panel.more": "Mai multe",
+ "tool-panel.shapes": "Forme",
+ "tool.arrow": "Săgeată",
+ "tool.arrow-down": "Săgeată în jos",
+ "tool.arrow-left": "Săgeată la stânga",
+ "tool.arrow-right": "Săgeată la dreapta",
+ "tool.arrow-up": "Săgeata în sus",
+ "tool.asset": "Resursă",
+ "tool.check-box": "Bifă",
+ "tool.cloud": "Cloud",
+ "tool.diamond": "Diamant",
+ "tool.draw": "Desenează",
+ "tool.ellipse": "Elipsă",
+ "tool.embed": "Înglobare",
+ "tool.eraser": "Radieră",
+ "tool.frame": "Cadru",
+ "tool.hand": "Mână",
+ "tool.hexagon": "Hexagon",
+ "tool.highlight": "Evidențiază",
+ "tool.laser": "Laser",
+ "tool.line": "Linie",
+ "tool.note": "Notă",
+ "tool.octagon": "Octogon",
+ "tool.oval": "Oval",
+ "tool.pentagon": "Pentagon",
+ "tool.rectangle": "Dreptunghi",
+ "tool.rhombus": "Romb",
+ "tool.select": "Selectează",
+ "tool.star": "Stea",
+ "tool.text": "Text",
+ "tool.trapezoid": "Trapez",
+ "tool.triangle": "Triunghi",
+ "tool.x-box": "Casetă X",
+ "verticalAlign-style.end": "Jos",
+ "verticalAlign-style.middle": "Mijloc",
+ "verticalAlign-style.start": "Sus",
+ "vscode.file-open.backup": "Copie de siguranță",
+ "vscode.file-open.backup-failed": "Eșec copie de siguranță: acesta nu este un fișier .tldr.",
+ "vscode.file-open.backup-saved": "Copie de siguranță salvată",
+ "vscode.file-open.desc": "Acest fișier a fost creat cu o versiune anterioară de tldraw. Vrei să îl actualizezi pentru a funcționa cu noua versiune?",
+ "vscode.file-open.dont-show-again": "Nu mai întreba",
+ "vscode.file-open.open": "Continuă"
+}
diff --git a/apps/web/public/translations/ru.json b/apps/web/public/translations/ru.json
new file mode 100644
index 00000000..a55749f0
--- /dev/null
+++ b/apps/web/public/translations/ru.json
@@ -0,0 +1,347 @@
+{
+ "action.align-bottom": "Выровнять по нижнему краю",
+ "action.align-center-horizontal": "Выровнять по горизонтали",
+ "action.align-center-horizontal.short": "Выровнять по горизонтали",
+ "action.align-center-vertical": "Выровнять по вертикали",
+ "action.align-center-vertical.short": "Выровнять по вертикали",
+ "action.align-left": "Выровнять по левому краю",
+ "action.align-right": "Выровнять по правому краю",
+ "action.align-top": "Выровнять по верхнему краю",
+ "action.back-to-content": "Назад к содержанию",
+ "action.bring-forward": "Переместить вперед",
+ "action.bring-to-front": "На передний план",
+ "action.convert-to-bookmark": "Конвертировать в закладку",
+ "action.convert-to-embed": "Конвертировать во встраивание",
+ "action.copy": "Копировать",
+ "action.copy-as-json": "Копировать как JSON",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "Копировать как PNG",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "Копировать как SVG",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "Вырезать",
+ "action.delete": "Удалить",
+ "action.distribute-horizontal": "Распределить по горизонтали",
+ "action.distribute-horizontal.short": "Распределить по горизонтали",
+ "action.distribute-vertical": "Распределить по вертикали",
+ "action.distribute-vertical.short": "Распределить по вертикали",
+ "action.duplicate": "Дублировать",
+ "action.edit-link": "Редактировать ссылку",
+ "action.exit-pen-mode": "Выйти из режима пера",
+ "action.export-as-json": "Экспортировать как JSON",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "Экспортировать как PNG",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "Экспортировать как SVG",
+ "action.export-as-svg.short": "SVG",
+ "action.flip-horizontal": "Зеркально отразить по горизонтали",
+ "action.flip-horizontal.short": "Зеркально отразить по горизонтали",
+ "action.flip-vertical": "Зеркально отразить по вертикали",
+ "action.flip-vertical.short": "Зеркально отразить по вертикали",
+ "action.fork-project": "Скопировать этот проект",
+ "action.group": "Сгруппировать",
+ "action.insert-embed": "Вставить встраивание",
+ "action.insert-media": "Загрузить медиа",
+ "action.leave-shared-project": "Выйти из совместного проекта",
+ "action.new-project": "Новый проект",
+ "action.new-shared-project": "Новый совместный проект",
+ "action.open-embed-link": "Открыть ссылку",
+ "action.open-file": "Открыть файл",
+ "action.pack": "Собрать в кучу",
+ "action.paste": "Вставить",
+ "action.print": "Печать",
+ "action.redo": "Повторить",
+ "action.rotate-ccw": "Вращать против часовой стрелки",
+ "action.rotate-cw": "Вращаться по часовой стрелке",
+ "action.save-copy": "Сохранить копию",
+ "action.select-all": "Выбрать всё",
+ "action.select-none": "Снять выделение",
+ "action.send-backward": "Переместить назад",
+ "action.send-to-back": "На задний план",
+ "action.share-project": "Поделиться этим проектом",
+ "action.stack-horizontal": "Разместить горизонтально",
+ "action.stack-horizontal.short": "Разместить горизонтально",
+ "action.stack-vertical": "Разместить вертикально",
+ "action.stack-vertical.short": "Разместить вертикально",
+ "action.stop-following": "Перестать следовать",
+ "action.stretch-horizontal": "Растянуть по горизонтали",
+ "action.stretch-horizontal.short": "Растянуть по горизонтали",
+ "action.stretch-vertical": "Растянуть по вертикали",
+ "action.stretch-vertical.short": "Растянуть по вертикали",
+ "action.toggle-auto-size": "Переключить автоматический размер",
+ "action.toggle-dark-mode": "Переключить темный режим",
+ "action.toggle-dark-mode.menu": "Темный режим",
+ "action.toggle-debug-mode": "Переключить режим отладки",
+ "action.toggle-debug-mode.menu": "Режим отладки",
+ "action.toggle-focus-mode": "Переключить режим концентрации",
+ "action.toggle-focus-mode.menu": "Режим концентрации",
+ "action.toggle-grid": "Переключить сетку",
+ "action.toggle-grid.menu": "Показать сетку",
+ "action.toggle-lock": "Блокировать / Разблокировать",
+ "action.toggle-snap-mode": "Переключить всегда привязываться",
+ "action.toggle-snap-mode.menu": "Всегда привязываться",
+ "action.toggle-tool-lock": "Переключить блокировку инструмента",
+ "action.toggle-tool-lock.menu": "Блокировка инструмента",
+ "action.toggle-transparent": "Переключить прозрачный фон",
+ "action.toggle-transparent.context-menu": "Прозрачный",
+ "action.toggle-transparent.menu": "Прозрачный",
+ "action.undo": "Отменить",
+ "action.ungroup": "Разгруппировать",
+ "action.unlock-all": "Разблокировать всё",
+ "action.zoom-in": "Увеличить",
+ "action.zoom-out": "Уменьшить",
+ "action.zoom-to-100": "Масштабировать до 100%",
+ "action.zoom-to-fit": "Масштабировать до размера окна",
+ "action.zoom-to-selection": "Масштабировать до выделения",
+ "actions-menu.title": "Действия",
+ "align-style.end": "По правому краю",
+ "align-style.justify": "По ширине",
+ "align-style.middle": "По центру",
+ "align-style.start": "По левому краю",
+ "arrowheadEnd-style.arrow": "Стрелка",
+ "arrowheadEnd-style.bar": "Линия",
+ "arrowheadEnd-style.diamond": "Ромб",
+ "arrowheadEnd-style.dot": "Круг",
+ "arrowheadEnd-style.inverted": "Обратная",
+ "arrowheadEnd-style.none": "Без",
+ "arrowheadEnd-style.pipe": "Труба",
+ "arrowheadEnd-style.square": "Квадрат",
+ "arrowheadEnd-style.triangle": "Треугольник",
+ "arrowheadStart-style.arrow": "Стрелка",
+ "arrowheadStart-style.bar": "Линия",
+ "arrowheadStart-style.diamond": "Ромб",
+ "arrowheadStart-style.dot": "Круг",
+ "arrowheadStart-style.inverted": "Обратная",
+ "arrowheadStart-style.none": "Без",
+ "arrowheadStart-style.pipe": "Труба",
+ "arrowheadStart-style.square": "Квадрат",
+ "arrowheadStart-style.triangle": "Треугольник",
+ "color-style.black": "Черный",
+ "color-style.blue": "Синий",
+ "color-style.green": "Зеленый",
+ "color-style.grey": "Серый",
+ "color-style.light-blue": "Голубой",
+ "color-style.light-green": "Светло-зеленый",
+ "color-style.light-red": "Светло-красный",
+ "color-style.light-violet": "Светло-фиолетовый",
+ "color-style.orange": "Оранжевый",
+ "color-style.red": "Красный",
+ "color-style.violet": "Фиолетовый",
+ "color-style.yellow": "Желтый",
+ "context-menu.arrange": "Организовать",
+ "context-menu.copy-as": "Скопировать как",
+ "context-menu.export-as": "Экспортировать как",
+ "context-menu.move-to-page": "Перенести на страницу",
+ "context-menu.reorder": "Переупорядочить",
+ "context.pages.new-page": "Новая страница",
+ "cursor-chat.type-to-chat": "Напишите сообщение...",
+ "dash-style.dashed": "Штриховой",
+ "dash-style.dotted": "Пунктирный",
+ "dash-style.draw": "Художественный",
+ "dash-style.solid": "Сплошной",
+ "debug-panel.more": "Подробнее",
+ "edit-link-dialog.cancel": "Отмена",
+ "edit-link-dialog.clear": "Очистить",
+ "edit-link-dialog.detail": "Ссылки откроются в новой вкладке.",
+ "edit-link-dialog.invalid-url": "Ссылка должна быть действительным URL-адресом.",
+ "edit-link-dialog.save": "Продолжить",
+ "edit-link-dialog.title": "Редактировать ссылку",
+ "edit-link-dialog.url": "URL-адрес",
+ "edit-pages-dialog.move-down": "Переместить вниз",
+ "edit-pages-dialog.move-up": "Переместить вверх",
+ "embed-dialog.back": "Назад",
+ "embed-dialog.cancel": "Отмена",
+ "embed-dialog.create": "Создать",
+ "embed-dialog.instruction": "Вставьте URL-адрес сайта, чтобы создать встраивание.",
+ "embed-dialog.invalid-url": "Нам не удалось создать встраивание из этого URL-адреса.",
+ "embed-dialog.title": "Создать встраивание",
+ "embed-dialog.url": "URL-адрес",
+ "file-system.confirm-clear.cancel": "Отмена",
+ "file-system.confirm-clear.continue": "Продолжить",
+ "file-system.confirm-clear.description": "Создание нового проекта очистит ваш текущий проект, и все несохраненные изменения будут потеряны. Вы уверены, что хотите продолжить?",
+ "file-system.confirm-clear.dont-show-again": "Больше не спрашивать",
+ "file-system.confirm-clear.title": "Очистить текущий проект?",
+ "file-system.confirm-open.cancel": "Отмена",
+ "file-system.confirm-open.description": "Открытие файла заменит ваш текущий проект, и все несохраненные изменения будут потеряны. Вы уверены, что хотите продолжить?",
+ "file-system.confirm-open.dont-show-again": "Больше не спрашивать",
+ "file-system.confirm-open.open": "Открыть файл",
+ "file-system.confirm-open.title": "Перезаписать текущий проект?",
+ "file-system.file-open-error.file-format-version-too-new": "Файл, который вы пытались открыть, относится к более новой версии tldraw. Пожалуйста, обновите страницу и попробуйте еще раз.",
+ "file-system.file-open-error.generic-corrupted-file": "Файл, который вы пытались открыть, поврежден.",
+ "file-system.file-open-error.not-a-tldraw-file": "Файл, который вы пытались открыть, не похож на файл tldraw.",
+ "file-system.file-open-error.title": "Не удалось открыть файл",
+ "file-system.shared-document-file-open-error.description": "Открытие файлов из совместных проектов не поддерживается.",
+ "file-system.shared-document-file-open-error.title": "Не удалось открыть файл",
+ "fill-style.none": "Без",
+ "fill-style.pattern": "Узор",
+ "fill-style.semi": "Полу",
+ "fill-style.solid": "Сплошная",
+ "focus-mode.toggle-focus-mode": "Переключить режим концентрации",
+ "font-style.draw": "Художественный",
+ "font-style.mono": "Моноширинный",
+ "font-style.sans": "Без засечек",
+ "font-style.serif": "С засечками",
+ "geo-style.arrow-down": "Стрелка вниз",
+ "geo-style.arrow-left": "Стрелка влево",
+ "geo-style.arrow-right": "Стрелка вправо",
+ "geo-style.arrow-up": "Стрелка вверх",
+ "geo-style.check-box": "Флажок",
+ "geo-style.cloud": "Облако",
+ "geo-style.diamond": "Ромб",
+ "geo-style.ellipse": "Эллипс",
+ "geo-style.hexagon": "Шестиугольник",
+ "geo-style.octagon": "Восьмиугольник",
+ "geo-style.oval": "Овал",
+ "geo-style.pentagon": "Пятиугольник",
+ "geo-style.rectangle": "Прямоугольник",
+ "geo-style.rhombus": "Ромб",
+ "geo-style.rhombus-2": "Ромб 2",
+ "geo-style.star": "Звезда",
+ "geo-style.trapezoid": "Трапеция",
+ "geo-style.triangle": "Треугольник",
+ "geo-style.x-box": "X квадрат",
+ "help-menu.about": "О нас",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "Сочетания клавиш",
+ "help-menu.title": "Помощь и материалы",
+ "help-menu.twitter": "Twitter",
+ "home-project-dialog.description": "Это ваш локальный домашний проект. Это только для вас!",
+ "home-project-dialog.ok": "Хорошо",
+ "home-project-dialog.title": "Домашний проект",
+ "menu.copy-as": "Скопировать как",
+ "menu.edit": "Правка",
+ "menu.export-as": "Экспортировать как",
+ "menu.file": "Файл",
+ "menu.language": "Язык",
+ "menu.preferences": "Настройки",
+ "menu.title": "Меню",
+ "menu.view": "Вид",
+ "navigation-zone.toggle-minimap": "Переключить мини-карту",
+ "navigation-zone.zoom": "Увеличить",
+ "opacity-style.0.1": "10%",
+ "opacity-style.0.25": "25%",
+ "opacity-style.0.5": "50%",
+ "opacity-style.0.75": "75%",
+ "opacity-style.1": "100%",
+ "page-menu.create-new-page": "Создать новую страницу",
+ "page-menu.edit-done": "Выполнено",
+ "page-menu.edit-start": "Редактировать",
+ "page-menu.go-to-page": "Перейти на страницу",
+ "page-menu.max-page-count-reached": "Достигнуто максимальное количество страниц",
+ "page-menu.new-page-initial-name": "Страница 1",
+ "page-menu.submenu.delete": "Удалить",
+ "page-menu.submenu.duplicate-page": "Дублировать",
+ "page-menu.submenu.move-down": "Переместить вниз",
+ "page-menu.submenu.move-up": "Переместить вверх",
+ "page-menu.submenu.rename": "Переименовать",
+ "page-menu.submenu.title": "Меню",
+ "page-menu.title": "Страницы",
+ "people-menu.change-color": "Изменить цвет",
+ "people-menu.change-name": "Изменить имя",
+ "people-menu.follow": "Следовать",
+ "people-menu.following": "Следуем за",
+ "people-menu.invite": "Пригласить других пользователей",
+ "people-menu.leading": "Следуют за Вами",
+ "people-menu.title": "Люди",
+ "people-menu.user": "(Вы)",
+ "rename-project-dialog.cancel": "Отмена",
+ "rename-project-dialog.rename": "Переименовать",
+ "rename-project-dialog.title": "Переименовать проект",
+ "share-menu.copy-link": "Копировать ссылку",
+ "share-menu.copy-link-note": "Любой, у кого есть ссылка, сможет просматривать и редактировать этот проект.",
+ "share-menu.copy-readonly-link": "Скопировать ссылку только для чтения",
+ "share-menu.copy-readonly-link-note": "Любой, у кого есть ссылка, сможет просматривать (но не редактировать) этот проект.",
+ "share-menu.create-snapshot-link": "Создать ссылку на снимок",
+ "share-menu.default-project-name": "Совместный проект",
+ "share-menu.fork-note": "Создайте новый общий проект на основе этого снимка.",
+ "share-menu.offline-note": "Совместное использование этого проекта создаст размещенную онлайн копию по новому URL-адресу. Вы можете поделиться URL-адресом с тридцатью другими людьми для совместного просмотра и редактирования проекта.",
+ "share-menu.project-too-large": "К сожалению, этим проектом нельзя поделиться, потому что он слишком большой. Мы работаем над этим!",
+ "share-menu.readonly-link": "Только для чтения",
+ "share-menu.save-note": "Загрузите этот проект на свой компьютер в виде .tldr файла.",
+ "share-menu.share-project": "Поделиться этим проектом",
+ "share-menu.snapshot-link-note": "Поделитесь ссылкой на этот проект в режиме только для чтения.",
+ "share-menu.title": "Поделиться",
+ "share-menu.upload-failed": "К сожалению, в данный момент мы не можем загрузить ваш проект. Повторите попытку или сообщите нам, если проблема не исчезнет.",
+ "sharing.confirm-leave.cancel": "Отмена",
+ "sharing.confirm-leave.description": "Вы уверены, что хотите покинуть этот совместный проект? Вы можете вернуться к нему, перейдя по его URL-адресу.",
+ "sharing.confirm-leave.dont-show-again": "Больше не спрашивать",
+ "sharing.confirm-leave.leave": "Выйти",
+ "sharing.confirm-leave.title": "Покинуть текущий проект?",
+ "shortcuts-dialog.edit": "Правка",
+ "shortcuts-dialog.file": "Файл",
+ "shortcuts-dialog.preferences": "Настройки",
+ "shortcuts-dialog.title": "Сочетания клавиш",
+ "shortcuts-dialog.tools": "Инструменты",
+ "shortcuts-dialog.transform": "Преобразования",
+ "shortcuts-dialog.view": "Вид",
+ "size-style.l": "Большой",
+ "size-style.m": "Средний",
+ "size-style.s": "Маленький",
+ "size-style.xl": "Очень большой",
+ "spline-style.cubic": "Кубический",
+ "spline-style.line": "Прямой",
+ "status.offline": "Не в сети",
+ "status.online": "В сети",
+ "style-panel.align": "Выровнять",
+ "style-panel.arrowhead-end": "Хвост",
+ "style-panel.arrowhead-start": "Наконечник",
+ "style-panel.arrowheads": "Стрелка",
+ "style-panel.color": "Цвет",
+ "style-panel.dash": "Контур",
+ "style-panel.fill": "Заливка",
+ "style-panel.font": "Шрифт",
+ "style-panel.geo": "Форма",
+ "style-panel.mixed": "Смешанный",
+ "style-panel.opacity": "Непрозрачность",
+ "style-panel.position": "Позиция",
+ "style-panel.size": "Размер",
+ "style-panel.spline": "Сплайн",
+ "style-panel.title": "Стили",
+ "style-panel.vertical-align": "Вертикальное выравнивание",
+ "toast.close": "Закрыть",
+ "toast.error.copy-fail.desc": "Не удалось скопировать изображение",
+ "toast.error.copy-fail.title": "Не удалось скопировать",
+ "toast.error.export-fail.desc": "Не удалось экспортировать изображение",
+ "toast.error.export-fail.title": "Ошибка экспорта",
+ "tool-panel.drawing": "Рисунок",
+ "tool-panel.more": "Подробнее",
+ "tool-panel.shapes": "Формы",
+ "tool.arrow": "Стрелка",
+ "tool.arrow-down": "Стрелка вниз",
+ "tool.arrow-left": "Стрелка влево",
+ "tool.arrow-right": "Стрелка вправо",
+ "tool.arrow-up": "Стрелка вверх",
+ "tool.asset": "Ресурс",
+ "tool.check-box": "Флажок",
+ "tool.cloud": "Облако",
+ "tool.diamond": "Ромбоид",
+ "tool.draw": "Карандаш",
+ "tool.ellipse": "Эллипс",
+ "tool.embed": "Встраивание",
+ "tool.eraser": "Ластик",
+ "tool.frame": "Рамка",
+ "tool.hand": "Рука",
+ "tool.hexagon": "Шестиугольник",
+ "tool.highlight": "Текстовыделитель",
+ "tool.laser": "Лазер",
+ "tool.line": "Линия",
+ "tool.note": "Заметка",
+ "tool.octagon": "Восьмиугольник",
+ "tool.oval": "Овал",
+ "tool.pentagon": "Пятиугольник",
+ "tool.rectangle": "Прямоугольник",
+ "tool.rhombus": "Ромб",
+ "tool.select": "Перемещение",
+ "tool.star": "Звезда",
+ "tool.text": "Текст",
+ "tool.trapezoid": "Трапеция",
+ "tool.triangle": "Треугольник",
+ "tool.x-box": "X квадрат",
+ "vscode.file-open.backup": "Резервная копия",
+ "vscode.file-open.backup-failed": "Резервное копирование не удалось: это не файл .tldr",
+ "vscode.file-open.backup-saved": "Резервная копия сохранена",
+ "vscode.file-open.desc": "Этот файл был создан в более ранней версии tldraw. Хотите обновить его для работы с новой версией?",
+ "vscode.file-open.dont-show-again": "Больше не спрашивать",
+ "vscode.file-open.open": "Продолжить"
+}
diff --git a/apps/web/public/translations/sl.json b/apps/web/public/translations/sl.json
new file mode 100644
index 00000000..c52c21e7
--- /dev/null
+++ b/apps/web/public/translations/sl.json
@@ -0,0 +1,373 @@
+{
+ "action.align-bottom": "Poravnaj dno",
+ "action.align-center-horizontal": "Poravnaj vodoravno",
+ "action.align-center-horizontal.short": "Poravnaj vodoravno",
+ "action.align-center-vertical": "Poravnaj navpično",
+ "action.align-center-vertical.short": "Poravnaj navpično",
+ "action.align-left": "Poravnaj levo",
+ "action.align-right": "Poravnaj desno",
+ "action.align-top": "Poravnaj vrh",
+ "action.back-to-content": "Nazaj na vsebino",
+ "action.bring-forward": "Premakni naprej",
+ "action.bring-to-front": "Premakni v ospredje",
+ "action.convert-to-bookmark": "Pretvori v zaznamek",
+ "action.convert-to-embed": "Pretvori v vdelavo",
+ "action.copy": "Kopiraj",
+ "action.copy-as-json": "Kopiraj kot JSON",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "Kopiraj kot PNG",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "Kopiraj kot SVG",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "Izreži",
+ "action.delete": "Izbriši",
+ "action.distribute-horizontal": "Porazdeli vodoravno",
+ "action.distribute-horizontal.short": "Porazdeli vodoravno",
+ "action.distribute-vertical": "Porazdeli navpično",
+ "action.distribute-vertical.short": "Porazdeli navpično",
+ "action.duplicate": "Podvoji",
+ "action.edit-link": "Uredi povezavo",
+ "action.exit-pen-mode": "Zapustite način peresa",
+ "action.export-all-as-json": "Izvozi vse kot JSON",
+ "action.export-all-as-json.short": "JSON",
+ "action.export-all-as-png": "Izvozi vse kot PNG",
+ "action.export-all-as-png.short": "PNG",
+ "action.export-all-as-svg": "Izvozi vse kot SVG",
+ "action.export-all-as-svg.short": "SVG",
+ "action.export-as-json": "Izvozi kot JSON",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "Izvozi kot PNG",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "Izvozi kot SVG",
+ "action.export-as-svg.short": "SVG",
+ "action.fit-frame-to-content": "Prilagodi vsebini",
+ "action.flip-horizontal": "Zrcali vodoravno",
+ "action.flip-horizontal.short": "Zrcali horizontalno",
+ "action.flip-vertical": "Zrcali navpično",
+ "action.flip-vertical.short": "Zrcali vertikalno",
+ "action.fork-project": "Naredi kopijo projekta",
+ "action.fork-project-on-tldraw": "Naredi kopijo na tldraw",
+ "action.group": "Združi",
+ "action.insert-embed": "Vstavi vdelavo",
+ "action.insert-media": "Naloži predstavnost",
+ "action.leave-shared-project": "Zapusti skupni projekt",
+ "action.new-project": "Nov projekt",
+ "action.new-shared-project": "Nov skupni projekt",
+ "action.open-cursor-chat": "Klepet s kazalcem",
+ "action.open-embed-link": "Odpri povezavo",
+ "action.open-file": "Odpri datoteko",
+ "action.pack": "Spakiraj",
+ "action.paste": "Prilepi",
+ "action.print": "Natisni",
+ "action.redo": "Uveljavi",
+ "action.remove-frame": "Odstrani okvir",
+ "action.rename": "Preimenuj",
+ "action.rotate-ccw": "Zavrti v nasprotni smeri urinega kazalca",
+ "action.rotate-cw": "Zavrti v smeri urinega kazalca",
+ "action.save-copy": "Shrani kopijo",
+ "action.select-all": "Izberi vse",
+ "action.select-none": "Počisti izbiro",
+ "action.send-backward": "Pošlji nazaj",
+ "action.send-to-back": "Pošlji v ozadje",
+ "action.share-project": "Deli ta projekt",
+ "action.stack-horizontal": "Naloži vodoravno",
+ "action.stack-horizontal.short": "Naloži vodoravno",
+ "action.stack-vertical": "Naloži navpično",
+ "action.stack-vertical.short": "Naloži navpično",
+ "action.stop-following": "Prenehaj slediti",
+ "action.stretch-horizontal": "Raztegnite vodoravno",
+ "action.stretch-horizontal.short": "Raztezanje vodoravno",
+ "action.stretch-vertical": "Raztegni navpično",
+ "action.stretch-vertical.short": "Raztezanje navpično",
+ "action.toggle-auto-size": "Preklopi samodejno velikost",
+ "action.toggle-dark-mode": "Preklopi temni način",
+ "action.toggle-dark-mode.menu": "Temni način",
+ "action.toggle-debug-mode": "Preklopi način odpravljanja napak",
+ "action.toggle-debug-mode.menu": "Način odpravljanja napak",
+ "action.toggle-edge-scrolling": "Preklopi pomikanje ob robovih",
+ "action.toggle-edge-scrolling.menu": "Pomikanje ob robovih",
+ "action.toggle-focus-mode": "Preklopi na osredotočen način",
+ "action.toggle-focus-mode.menu": "Osredotočen način",
+ "action.toggle-grid": "Preklopi mrežo",
+ "action.toggle-grid.menu": "Prikaži mrežo",
+ "action.toggle-lock": "Zakleni / odkleni",
+ "action.toggle-reduce-motion": "Preklop zmanjšanja gibanja",
+ "action.toggle-reduce-motion.menu": "Zmanjšaj gibanje",
+ "action.toggle-snap-mode": "Preklopi pripenjanje",
+ "action.toggle-snap-mode.menu": "Vedno pripni",
+ "action.toggle-tool-lock": "Preklopi zaklepanje orodja",
+ "action.toggle-tool-lock.menu": "Zaklepanje orodja",
+ "action.toggle-transparent": "Preklopi prosojno ozadje",
+ "action.toggle-transparent.context-menu": "Prozorno",
+ "action.toggle-transparent.menu": "Prozorno",
+ "action.toggle-wrap-mode": "Preklopi Izberi ob zajetju",
+ "action.toggle-wrap-mode.menu": "Izberi ob zajetju",
+ "action.undo": "Razveljavi",
+ "action.ungroup": "Razdruži",
+ "action.unlock-all": "Odkleni vse",
+ "action.zoom-in": "Povečaj",
+ "action.zoom-out": "Pomanjšaj",
+ "action.zoom-to-100": "Povečaj na 100 %",
+ "action.zoom-to-fit": "Povečaj do prileganja",
+ "action.zoom-to-selection": "Pomakni na izbiro",
+ "actions-menu.title": "Akcije",
+ "align-style.end": "Konec",
+ "align-style.justify": "Poravnaj",
+ "align-style.middle": "Sredina",
+ "align-style.start": "Začetek",
+ "arrowheadEnd-style.arrow": "Puščica",
+ "arrowheadEnd-style.bar": "Črta",
+ "arrowheadEnd-style.diamond": "Diamant",
+ "arrowheadEnd-style.dot": "Pika",
+ "arrowheadEnd-style.inverted": "Obrnjeno",
+ "arrowheadEnd-style.none": "Brez",
+ "arrowheadEnd-style.pipe": "Cev",
+ "arrowheadEnd-style.square": "Kvadrat",
+ "arrowheadEnd-style.triangle": "Trikotnik",
+ "arrowheadStart-style.arrow": "Puščica",
+ "arrowheadStart-style.bar": "Črta",
+ "arrowheadStart-style.diamond": "Diamant",
+ "arrowheadStart-style.dot": "Pika",
+ "arrowheadStart-style.inverted": "Obrnjeno",
+ "arrowheadStart-style.none": "Brez",
+ "arrowheadStart-style.pipe": "Cev",
+ "arrowheadStart-style.square": "Kvadrat",
+ "arrowheadStart-style.triangle": "Trikotnik",
+ "assets.files.upload-failed": "Nalaganje ni uspelo",
+ "assets.url.failed": "Ni bilo mogoče naložiti predogleda URL",
+ "color-style.black": "Črna",
+ "color-style.blue": "Modra",
+ "color-style.green": "Zelena",
+ "color-style.grey": "Siva",
+ "color-style.light-blue": "Svetlo modra",
+ "color-style.light-green": "Svetlo zelena",
+ "color-style.light-red": "Svetlo rdeča",
+ "color-style.light-violet": "Svetlo vijolična",
+ "color-style.orange": "Oranžna",
+ "color-style.red": "Rdeča",
+ "color-style.violet": "Vijolična",
+ "color-style.white": "Bela",
+ "color-style.yellow": "Rumena",
+ "context-menu.arrange": "Preuredi",
+ "context-menu.copy-as": "Kopiraj kot",
+ "context-menu.export-all-as": "Izvozi vse kot",
+ "context-menu.export-as": "Izvozi kot",
+ "context-menu.move-to-page": "Premakni na stran",
+ "context-menu.reorder": "Preuredite",
+ "context.pages.new-page": "Nova stran",
+ "cursor-chat.type-to-chat": "Vnesite za klepet ...",
+ "dash-style.dashed": "Črtkano",
+ "dash-style.dotted": "Pikčasto",
+ "dash-style.draw": "Narisano",
+ "dash-style.solid": "Polno",
+ "debug-panel.more": "Več",
+ "document.default-name": "Neimenovana",
+ "edit-link-dialog.cancel": "Prekliči",
+ "edit-link-dialog.clear": "Počisti",
+ "edit-link-dialog.detail": "Povezave se bodo odprle v novem zavihku.",
+ "edit-link-dialog.invalid-url": "Povezava mora biti veljavna",
+ "edit-link-dialog.save": "Nadaljuj",
+ "edit-link-dialog.title": "Uredi povezavo",
+ "edit-link-dialog.url": "URL",
+ "edit-pages-dialog.move-down": "Premakni navzdol",
+ "edit-pages-dialog.move-up": "Premakni navzgor",
+ "embed-dialog.back": "Nazaj",
+ "embed-dialog.cancel": "Prekliči",
+ "embed-dialog.create": "Ustvari",
+ "embed-dialog.instruction": "Prilepite URL spletnega mesta, da ustvarite vdelavo.",
+ "embed-dialog.invalid-url": "Iz tega URL-ja nismo mogli ustvariti vdelave.",
+ "embed-dialog.title": "Ustvari vdelavo",
+ "embed-dialog.url": "URL",
+ "file-system.confirm-clear.cancel": "Prekliči",
+ "file-system.confirm-clear.continue": "Nadaljuj",
+ "file-system.confirm-clear.description": "Če ustvarite nov projekt, boste izbrisali trenutni projekt in vse neshranjene spremembe bodo izgubljene. Ste prepričani, da želite nadaljevati?",
+ "file-system.confirm-clear.dont-show-again": "Ne sprašuj znova",
+ "file-system.confirm-clear.title": "Počisti trenutni projekt?",
+ "file-system.confirm-open.cancel": "Prekliči",
+ "file-system.confirm-open.description": "Če ustvarite nov projekt, boste izbrisali trenutni projekt in vse neshranjene spremembe bodo izgubljene. Ste prepričani, da želite nadaljevati?",
+ "file-system.confirm-open.dont-show-again": "Ne sprašuj znova",
+ "file-system.confirm-open.open": "Odpri datoteko",
+ "file-system.confirm-open.title": "Prepiši trenutni projekt?",
+ "file-system.file-open-error.file-format-version-too-new": "Datoteka, ki ste jo poskušali odpreti, je iz novejše različice tldraw. Ponovno naložite stran in poskusite znova.",
+ "file-system.file-open-error.generic-corrupted-file": "Datoteka, ki ste jo poskušali odpreti, je poškodovana.",
+ "file-system.file-open-error.not-a-tldraw-file": "Datoteka, ki ste jo poskušali odpreti, ni videti kot datoteka tldraw.",
+ "file-system.file-open-error.title": "Datoteke ni bilo mogoče odpreti",
+ "file-system.shared-document-file-open-error.description": "Odpiranje datotek v skupnih projektih ni podprto.",
+ "file-system.shared-document-file-open-error.title": "Datoteke ni bilo mogoče odpreti",
+ "fill-style.none": "Brez",
+ "fill-style.pattern": "Vzorec",
+ "fill-style.semi": "Polovično",
+ "fill-style.solid": "Polno",
+ "focus-mode.toggle-focus-mode": "Preklopi na osredotočen način",
+ "font-style.draw": "Draw",
+ "font-style.mono": "Mono",
+ "font-style.sans": "Sans",
+ "font-style.serif": "Serif",
+ "geo-style.arrow-down": "Puščica navzdol",
+ "geo-style.arrow-left": "Puščica levo",
+ "geo-style.arrow-right": "Puščica desno",
+ "geo-style.arrow-up": "Puščica navzgor",
+ "geo-style.check-box": "Potrditveno polje",
+ "geo-style.cloud": "Oblak",
+ "geo-style.diamond": "Diamant",
+ "geo-style.ellipse": "Elipsa",
+ "geo-style.hexagon": "Šesterokotnik",
+ "geo-style.octagon": "Osmerokotnik",
+ "geo-style.oval": "Oval",
+ "geo-style.pentagon": "Peterokotnik",
+ "geo-style.rectangle": "Pravokotnik",
+ "geo-style.rhombus": "Romb",
+ "geo-style.rhombus-2": "Romb 2",
+ "geo-style.star": "Zvezda",
+ "geo-style.trapezoid": "Trapez",
+ "geo-style.triangle": "Trikotnik",
+ "geo-style.x-box": "X polje",
+ "help-menu.about": "O nas",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "Bližnjice na tipkovnici",
+ "help-menu.title": "Pomoč in viri",
+ "help-menu.twitter": "Twitter",
+ "home-project-dialog.description": "To je vaš lokalni projekt. Namenjen je samo vam!",
+ "home-project-dialog.ok": "V redu",
+ "home-project-dialog.title": "Lokalni projekt",
+ "menu.copy-as": "Kopiraj kot",
+ "menu.edit": "Uredi",
+ "menu.export-as": "Izvozi kot",
+ "menu.file": "Datoteka",
+ "menu.language": "Jezik",
+ "menu.preferences": "Nastavitve",
+ "menu.title": "Meni",
+ "menu.view": "Pogled",
+ "navigation-zone.toggle-minimap": "Preklopi mini zemljevid",
+ "navigation-zone.zoom": "Povečava",
+ "opacity-style.0.1": "10 %",
+ "opacity-style.0.25": "25 %",
+ "opacity-style.0.5": "50 %",
+ "opacity-style.0.75": "75 %",
+ "opacity-style.1": "100 %",
+ "page-menu.create-new-page": "Ustvari novo stran",
+ "page-menu.edit-done": "Zaključi",
+ "page-menu.edit-start": "Uredi",
+ "page-menu.go-to-page": "Pojdi na stran",
+ "page-menu.max-page-count-reached": "Doseženo največje število strani",
+ "page-menu.new-page-initial-name": "Stran 1",
+ "page-menu.submenu.delete": "Izbriši",
+ "page-menu.submenu.duplicate-page": "Podvoji",
+ "page-menu.submenu.move-down": "Premakni navzdol",
+ "page-menu.submenu.move-up": "Premakni navzgor",
+ "page-menu.submenu.rename": "Preimenuj",
+ "page-menu.submenu.title": "Meni",
+ "page-menu.title": "Strani",
+ "people-menu.change-color": "Spremeni barvo",
+ "people-menu.change-name": "Spremeni ime",
+ "people-menu.follow": "Sledi",
+ "people-menu.following": "Sledim",
+ "people-menu.invite": "Povabi ostale",
+ "people-menu.leading": "Sledi vam",
+ "people-menu.title": "Ljudje",
+ "people-menu.user": "(Ti)",
+ "rename-project-dialog.cancel": "Prekliči",
+ "rename-project-dialog.rename": "Preimenuj",
+ "rename-project-dialog.title": "Preimenuj projekt",
+ "share-menu.copy-link": "Kopiraj povezavo",
+ "share-menu.copy-link-note": "Vsakdo s povezavo si bo lahko ogledal in urejal ta projekt.",
+ "share-menu.copy-readonly-link": "Kopiraj povezavo samo za branje",
+ "share-menu.copy-readonly-link-note": "Vsakdo s povezavo si bo lahko ogledal (vendar ne urejal) ta projekt.",
+ "share-menu.create-snapshot-link": "Ustvari povezavo do posnetka",
+ "share-menu.default-project-name": "Skupni projekt",
+ "share-menu.fork-note": "Na podlagi tega posnetka ustvarite nov skupni projekt.",
+ "share-menu.offline-note": "Skupna raba tega projekta bo ustvarila živo kopijo na novem URL-ju. URL lahko delite z do tridesetimi drugimi osebami, s katerimi lahko skupaj gledate in urejate vsebino.",
+ "share-menu.project-too-large": "Žal tega projekta ni mogoče deliti, ker je prevelik. Delamo na tem!",
+ "share-menu.readonly-link": "Samo za branje",
+ "share-menu.save-note": "Ta projekt prenesite na svoj računalnik kot datoteko .tldr.",
+ "share-menu.share-project": "Deli ta projekt",
+ "share-menu.snapshot-link-note": "Zajemite in delite ta projekt kot povezavo do posnetka samo za branje.",
+ "share-menu.title": "Deli",
+ "share-menu.upload-failed": "Oprostite, trenutno nismo mogli naložiti vašega projekta. Poskusite znova ali nam sporočite, če se težava ponovi.",
+ "sharing.confirm-leave.cancel": "Prekliči",
+ "sharing.confirm-leave.description": "Ali ste prepričani, da želite zapustiti ta skupni projekt? Nanj se lahko vrnete tako, da se ponovno vrnete na njegov URL.",
+ "sharing.confirm-leave.dont-show-again": "Ne sprašuj znova",
+ "sharing.confirm-leave.leave": "Zapusti",
+ "sharing.confirm-leave.title": "Zapusti trenutni projekt?",
+ "shortcuts-dialog.collaboration": "Sodelovanje",
+ "shortcuts-dialog.edit": "Uredi",
+ "shortcuts-dialog.file": "Datoteka",
+ "shortcuts-dialog.preferences": "Nastavitve",
+ "shortcuts-dialog.title": "Bližnjice na tipkovnici",
+ "shortcuts-dialog.tools": "Orodja",
+ "shortcuts-dialog.transform": "Preoblikuj",
+ "shortcuts-dialog.view": "Pogled",
+ "size-style.l": "Veliko",
+ "size-style.m": "Srednje",
+ "size-style.s": "Malo",
+ "size-style.xl": "Zelo veliko",
+ "spline-style.cubic": "Kubično",
+ "spline-style.line": "Črta",
+ "status.offline": "Brez povezave",
+ "status.online": "Povezan",
+ "style-panel.align": "Poravnava",
+ "style-panel.arrowhead-end": "Konec",
+ "style-panel.arrowhead-start": "Začetek",
+ "style-panel.arrowheads": "Puščice",
+ "style-panel.color": "Barva",
+ "style-panel.dash": "Črtasto",
+ "style-panel.fill": "Polnilo",
+ "style-panel.font": "Pisava",
+ "style-panel.geo": "Oblika",
+ "style-panel.mixed": "Mešano",
+ "style-panel.opacity": "Motnost",
+ "style-panel.position": "Položaj",
+ "style-panel.size": "Velikost",
+ "style-panel.spline": "Krivulja",
+ "style-panel.title": "Stili",
+ "style-panel.vertical-align": "Navpična poravnava",
+ "toast.close": "Zapri",
+ "toast.error.copy-fail.desc": "Kopiranje slike ni uspelo",
+ "toast.error.copy-fail.title": "Kopiranje ni uspelo",
+ "toast.error.export-fail.desc": "Izvoz slike ni uspel",
+ "toast.error.export-fail.title": "Izvoz ni uspel",
+ "tool-panel.drawing": "Risanje",
+ "tool-panel.more": "Več",
+ "tool-panel.shapes": "Oblike",
+ "tool.arrow": "Puščica",
+ "tool.arrow-down": "Puščica navzdol",
+ "tool.arrow-left": "Puščica levo",
+ "tool.arrow-right": "Puščica desno",
+ "tool.arrow-up": "Puščica navzgor",
+ "tool.asset": "Sredstvo",
+ "tool.check-box": "Potrditveno polje",
+ "tool.cloud": "Oblak",
+ "tool.diamond": "Diamant",
+ "tool.draw": "Risanje",
+ "tool.ellipse": "Elipsa",
+ "tool.embed": "Vdelava",
+ "tool.eraser": "Radirka",
+ "tool.frame": "Okvir",
+ "tool.hand": "Roka",
+ "tool.hexagon": "Šesterokotnik",
+ "tool.highlight": "Marker",
+ "tool.laser": "Laser",
+ "tool.line": "Črta",
+ "tool.note": "Opomba",
+ "tool.octagon": "Osmerokotnik",
+ "tool.oval": "Oval",
+ "tool.pentagon": "Peterokotnik",
+ "tool.rectangle": "Pravokotnik",
+ "tool.rhombus": "Romb",
+ "tool.select": "Izbor",
+ "tool.star": "Zvezda",
+ "tool.text": "Besedilo",
+ "tool.trapezoid": "Trapez",
+ "tool.triangle": "Trikotnik",
+ "tool.x-box": "X polje",
+ "verticalAlign-style.end": "Dno",
+ "verticalAlign-style.middle": "Sredina",
+ "verticalAlign-style.start": "Vrh",
+ "vscode.file-open.backup": "Varnostna kopija",
+ "vscode.file-open.backup-failed": "Varnostno kopiranje ni uspelo: to ni datoteka .tldr.",
+ "vscode.file-open.backup-saved": "Varnostna kopija shranjena",
+ "vscode.file-open.desc": "Ta datoteka je bila ustvarjena s starejšo različico tldraw. Ali jo želite posodobiti, da bo deloval z novo različico?",
+ "vscode.file-open.dont-show-again": "Ne sprašuj znova",
+ "vscode.file-open.open": "Nadaljuj"
+}
diff --git a/apps/web/public/translations/sv.json b/apps/web/public/translations/sv.json
new file mode 100644
index 00000000..fa965f2d
--- /dev/null
+++ b/apps/web/public/translations/sv.json
@@ -0,0 +1,96 @@
+{
+ "action.bring-forward": "Flytta framåt",
+ "action.bring-to-front": "Placera längst fram",
+ "action.copy": "Kopiera",
+ "action.cut": "Klipp ut",
+ "action.delete": "Radera",
+ "action.duplicate": "Duplicera",
+ "action.flip-horizontal": "Vänd horisontellt",
+ "action.flip-vertical": "Vänd vertikalt",
+ "action.flip-horizontal.short": "Vänd horisontellt",
+ "action.flip-vertical.short": "Vänd vertikalt",
+ "action.group": "Gruppera",
+ "action.insert-media": "Ladda upp media",
+ "action.paste": "Klistra in",
+ "action.redo": "Gör om",
+ "action.select-all": "Välj alla",
+ "action.select-none": "Välj ingen",
+ "action.send-backward": "Flytta bakåt",
+ "action.send-to-back": "Placera längst bak",
+ "action.toggle-dark-mode.menu": "Mörkt läge",
+ "action.toggle-dark-mode": "Mörkt läge",
+ "action.toggle-debug-mode.menu": "Debugläge",
+ "action.toggle-debug-mode": "Debugläge",
+ "action.toggle-focus-mode.menu": "Fokusläge",
+ "action.toggle-focus-mode": "Fokusläge",
+ "action.toggle-grid.menu": "Visa rutnät",
+ "action.toggle-grid": "Visa rutnät",
+ "action.toggle-snap-mode.menu": "Visa alltid fästpunkter",
+ "action.toggle-snap-mode": "Visa alltid fästpunkter",
+ "action.toggle-transparent.context-menu": "Transparent",
+ "action.toggle-transparent.menu": "Transparent",
+ "action.undo": "Ångra",
+ "action.ungroup": "Avgruppera",
+ "action.zoom-in": "Zooma in",
+ "action.zoom-out": "Zooma ut",
+ "action.zoom-to-fit": "Anpassa zoom till skärm",
+ "action.zoom-to-selection": "Anpassa zoom till urval",
+ "dash-style.draw": "Rita",
+ "font-style.draw": "Rita",
+ "geo-style.ellipse": "Ellips",
+ "geo-style.rectangle": "Rektangel",
+ "geo-style.triangle": "Triangel",
+ "arrowheadStart-style.arrow": "Pil",
+ "arrowheadStart-style.triangle": "Triangel",
+ "arrowheadEnd-style.arrow": "Pil",
+ "arrowheadEnd-style.triangle": "Triangel",
+ "spline-style.line": "Linje",
+ "tool.select": "Välj",
+ "tool.draw": "Rita",
+ "tool.eraser": "Radera",
+ "tool.arrow": "Pil",
+ "tool.ellipse": "Ellips",
+ "tool.line": "Linje",
+ "tool.rectangle": "Rektangel",
+ "tool.triangle": "Triangel",
+ "tool.note": "Klisterlapp",
+ "tool.text": "Text",
+ "menu.copy-as": "Kopiera som",
+ "menu.edit": "Redigera",
+ "menu.export-as": "Exportera till",
+ "menu.file": "Arkiv",
+ "menu.language": "Språk",
+ "menu.preferences": "Inställningar",
+ "menu.view": "Innehåll",
+ "context-menu.copy-as": "Kopiera som",
+ "context-menu.export-as": "Exportera till",
+ "context-menu.move-to-page": "Flytta till sida",
+ "page-menu.create-new-page": "Skapa sida",
+ "page-menu.edit-start": "Redigera",
+ "page-menu.submenu.duplicate-page": "Duplicera",
+ "page-menu.submenu.delete": "Radera",
+ "share-menu.copy-link": "Kopiera länk med redigeringsrättigheter",
+ "share-menu.copy-readonly-link": "Kopiera länk med läsrättigheter",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "Tangentbordsgenvägar",
+ "help-menu.twitter": "Twitter",
+ "edit-link-dialog.cancel": "Avbryt",
+ "embed-dialog.cancel": "Avbryt",
+ "shortcuts-dialog.title": "Tangentbordsgenvägar",
+ "shortcuts-dialog.edit": "Redigera",
+ "shortcuts-dialog.file": "Arkiv",
+ "shortcuts-dialog.preferences": "Inställningar",
+ "shortcuts-dialog.tools": "Verktyg",
+ "shortcuts-dialog.transform": "Transform",
+ "shortcuts-dialog.view": "Innehåll",
+ "style-panel.title": "Utseende",
+ "style-panel.align": "Justera",
+ "style-panel.color": "Färg",
+ "style-panel.dash": "Streck",
+ "style-panel.fill": "Ifylld",
+ "style-panel.font": "Typsnitt",
+ "style-panel.size": "Storlek",
+ "focus-mode.toggle-focus-mode": "Fokusläge",
+ "file-system.confirm-open.cancel": "Avbryt"
+}
diff --git a/apps/web/public/translations/te.json b/apps/web/public/translations/te.json
new file mode 100644
index 00000000..d5f64f4b
--- /dev/null
+++ b/apps/web/public/translations/te.json
@@ -0,0 +1,112 @@
+{
+ "action.align-bottom": "అడుగున కుదుర్చు",
+ "action.align-center-horizontal": "అడ్డంగా మధ్యలో కుదుర్చు",
+ "action.align-center-vertical": "నిలువుగా మధ్యలో కుదుర్చు",
+ "action.align-center-horizontal.short": "అడ్డంగా మధ్యలో కుదుర్చు",
+ "action.align-center-vertical.short": "నిలువుగా మధ్యలో కుదుర్చు",
+ "action.align-left": "ఎడమవైపుకు కుదుర్చు",
+ "action.align-right": "కుడివైపుకు కుదుర్చు",
+ "action.align-top": "పైకి కుదుర్చు",
+ "action.bring-forward": "ముందుకు జరుపు",
+ "action.bring-to-front": "మొదటికి జరుపు",
+ "action.copy": "నకలు",
+ "action.cut": "కత్తిరించు",
+ "action.delete": "తొలగించు",
+ "action.distribute-horizontal": "అడ్డంగా పంచు",
+ "action.distribute-vertical": "నిలువుగా పంచు",
+ "action.distribute-horizontal.short": "అడ్డంగా పంచు",
+ "action.distribute-vertical.short": "నిలువుగా పంచు",
+ "action.duplicate": "మారుప్రతి",
+ "action.flip-horizontal": "అడ్డంగా పల్టీ",
+ "action.flip-vertical": "నిలువుగా పల్టీ",
+ "action.flip-horizontal.short": "అడ్డంగా పల్టీ",
+ "action.flip-vertical.short": "నిలువుగా పల్టీ",
+ "action.group": "గుంపుగా ఏర్పరచు",
+ "action.insert-media": "ఎగుమతి మాధ్యం",
+ "action.paste": "అతికించు",
+ "action.redo": "మళ్ళీ మార్చు",
+ "action.select-all": "అన్నీ ఎంపికచెయ్యి",
+ "action.select-none": "ఎదీ ఎంపికచెయ్యవద్దు",
+ "action.send-backward": "వెనుకకు జరుపు",
+ "action.send-to-back": "ఆఖరికి జరుపు",
+ "action.stretch-horizontal": "అడ్డంగా లాగు",
+ "action.stretch-vertical": "నిలువుగా లాగు",
+ "action.stretch-horizontal.short": "అడ్డంగా లాగు",
+ "action.stretch-vertical.short": "నిలువుగా లాగు",
+ "action.toggle-dark-mode.menu": "చీకటైన క్రమం",
+ "action.toggle-dark-mode": "చీకటైన క్రమం",
+ "action.toggle-debug-mode.menu": "తప్పులేరు క్రమం",
+ "action.toggle-debug-mode": "తప్పులేరు క్రమం",
+ "action.toggle-focus-mode.menu": "తీక్షణ క్రమం",
+ "action.toggle-focus-mode": "తీక్షణ క్రమం",
+ "action.toggle-grid.menu": "చట్రం చూపు",
+ "action.toggle-grid": "చట్రం చూపు",
+ "action.toggle-snap-mode.menu": "ఎప్పుడూ దృశ్యభాగం చూపు",
+ "action.toggle-snap-mode": "ఎప్పుడూ దృశ్యభాగం చూపు",
+ "action.toggle-transparent.context-menu": "కాంతి భేద్యము",
+ "action.toggle-transparent.menu": "కాంతి భేద్యము",
+ "action.undo": "మార్పుని తిరుగగొట్టు",
+ "action.ungroup": "గుంపును చెదరగొట్టు",
+ "action.zoom-in": "దగ్గరగా చూపు",
+ "action.zoom-out": "దూరంగా చూపు",
+ "action.zoom-to-fit": "సరిపెట్టి చూపు",
+ "action.zoom-to-selection": "ఎంచుకున్న విషయాన్నే చూపు",
+ "dash-style.draw": "గీయ్యి",
+ "font-style.draw": "గీయ్యి",
+ "geo-style.ellipse": "దీర్ఘవృత్తం",
+ "geo-style.rectangle": "దీర్ఘచతురస్రం",
+ "geo-style.triangle": "త్రిభుజం",
+ "arrowheadStart-style.arrow": "బాణం",
+ "arrowheadStart-style.triangle": "త్రిభుజం",
+ "arrowheadEnd-style.arrow": "బాణం",
+ "arrowheadEnd-style.triangle": "త్రిభుజం",
+ "spline-style.line": "గీత",
+ "tool.select": "ఎంపికచెయ్యి",
+ "tool.draw": "గీయ్యి",
+ "tool.eraser": "Eraser",
+ "tool.arrow": "బాణం",
+ "tool.ellipse": "దీర్ఘవృత్తం",
+ "tool.line": "గీత",
+ "tool.rectangle": "దీర్ఘచతురస్రం",
+ "tool.triangle": "త్రిభుజం",
+ "tool.note": "అతుక్కునే",
+ "tool.text": "అక్షరములు",
+ "menu.copy-as": "నకలుప్రతిగా ఇక్కడికి",
+ "menu.edit": "పరిష్కరించు",
+ "menu.export-as": "ఎగుమతి ఇక్కడికి",
+ "menu.file": "ఫైల్",
+ "menu.language": "భాష",
+ "menu.preferences": "ఎంచుకొన్నవి",
+ "menu.view": "చూపు",
+ "context-menu.copy-as": "నకలుప్రతిగా ఇక్కడికి",
+ "context-menu.export-as": "ఎగుమతి ఇక్కడికి",
+ "context-menu.move-to-page": "పుటలోకి జరుపు",
+ "page-menu.create-new-page": "పుట కల్పించు",
+ "page-menu.edit-start": "పరిష్కరించు",
+ "page-menu.submenu.duplicate-page": "మారుప్రతి",
+ "page-menu.submenu.delete": "తొలగించు",
+ "share-menu.copy-link": "అహ్వాన లింకు రాయి",
+ "share-menu.copy-readonly-link": "మారనిప్రతి లింకు రాయి",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "కీబోర్డ్ సత్వరమార్గం",
+ "help-menu.twitter": "Twitter",
+ "edit-link-dialog.cancel": "రద్దుచేయి",
+ "embed-dialog.cancel": "రద్దుచేయి",
+ "shortcuts-dialog.title": "కీబోర్డ్ సత్వరమార్గం",
+ "shortcuts-dialog.edit": "పరిష్కరించు",
+ "shortcuts-dialog.file": "ఫైల్",
+ "shortcuts-dialog.preferences": "ఎంచుకొన్నవి",
+ "shortcuts-dialog.tools": "పరికరాలు",
+ "shortcuts-dialog.transform": "మార్చు",
+ "shortcuts-dialog.view": "చూపు",
+ "style-panel.title": "విధములు",
+ "style-panel.align": "సరిపరచు",
+ "style-panel.color": "రంగు",
+ "style-panel.dash": "అడ్డ గీత",
+ "style-panel.fill": "నింపు",
+ "style-panel.font": "అక్షరాకృతి",
+ "style-panel.size": "పరిమాణం",
+ "focus-mode.toggle-focus-mode": "తీక్షణ క్రమం",
+ "file-system.confirm-open.cancel": "రద్దుచేయి"
+}
diff --git a/apps/web/public/translations/th.json b/apps/web/public/translations/th.json
new file mode 100644
index 00000000..fa792d42
--- /dev/null
+++ b/apps/web/public/translations/th.json
@@ -0,0 +1,295 @@
+{
+ "action.align-bottom": "จัดให้ชิดด้านล่าง",
+ "action.align-center-horizontal": "จัดให้กึ่งกลางแนวนอน",
+ "action.align-center-horizontal.short": "จัดให้อยู่กึ่งกลางแนวนอน",
+ "action.align-center-vertical": "ซ้อนต่อกันแนวนอน",
+ "action.align-center-vertical.short": "จัดให้อยู่กึ่งกลางแนวตั้ง",
+ "action.align-left": "จัดให้ชิดซ้าย",
+ "action.align-right": "จัดให้ชิดขวา",
+ "action.align-top": "จัดให้ชิดด้านบน",
+ "action.back-to-content": "กลับไปยังเนื้อหา",
+ "action.bring-forward": "ย้ายไปข้างหน้า",
+ "action.bring-to-front": "ย้ายไปด้านหน้าสุด",
+ "action.convert-to-bookmark": "แปลงเป็นบุ๊กมาร์ก",
+ "action.convert-to-embed": "แปลงเป็น Embed",
+ "action.copy": "คัดลอก",
+ "action.copy-as-json": "คัดลอกเป็น JSON",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "คัดลอกเป็น PNG",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "คัดลอกเป็น SVG",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "ตัด",
+ "action.delete": "ลบ",
+ "action.distribute-horizontal": "กระจายในแนวนอน",
+ "action.distribute-horizontal.short": "กระจายแนวนอน",
+ "action.distribute-vertical": "กระจายในแนวตั้ง",
+ "action.distribute-vertical.short": "กระจายแนวตั้ง",
+ "action.duplicate": "ทำซ้ำ",
+ "action.edit-link": "แก้ไขลิงก์",
+ "action.exit-pen-mode": "ออกจากโหมดปากกา",
+ "action.export-as-json": "ส่งออกเป็น JSON",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "ส่งออกเป็น PNG",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "ส่งออกเป็น SVG",
+ "action.export-as-svg.short": "SVG",
+ "action.flip-horizontal": "พลิกแนวนอน",
+ "action.flip-horizontal.short": "พลิกแนวตั้ง",
+ "action.flip-vertical": "พลิกแนวตั้ง",
+ "action.flip-vertical.short": "พลิกแนวนอน",
+ "action.group": "จัดกลุ่ม",
+ "action.insert-media": "อัปโหลดสื่อ",
+ "action.new-shared-project": "โครงการใหม่ที่ใช้ร่วมกัน",
+ "action.open-embed-link": "เปิดลิงก์",
+ "action.open-file": "เปิดไฟล์",
+ "action.pack": "บรรจุ",
+ "action.paste": "วาง",
+ "action.print": "พิมพ์",
+ "action.redo": "ทำซ้ำ",
+ "action.rotate-ccw": "หมุนทวนเข็มนาฬิกา",
+ "action.rotate-cw": "หมุนตามเข็มนาฬิกา",
+ "action.save-copy": "บันทึกสำเนา",
+ "action.select-all": "เลือกทั้งหมด",
+ "action.select-none": "ไม่เลือกทั้งหมด",
+ "action.send-backward": "ย้ายไปข้างหลัง",
+ "action.send-to-back": "ย้ายไปด้านหลังสุด",
+ "action.share-project": "แบ่งปันโครงการนี้",
+ "action.stack-horizontal": "ซ้อนต่อกันแนวนอน",
+ "action.stack-horizontal.short": "ซ้อนต่อกันแนวนอน",
+ "action.stack-vertical": "ซ้อนต่อกันแนวตั้ง",
+ "action.stack-vertical.short": "ซ้อนต่อกันแนวตั้ง",
+ "action.stretch-horizontal": "ยืดในแนวนอน",
+ "action.stretch-horizontal.short": "ยืดแนวนอน",
+ "action.stretch-vertical": "ยืดในแนวตั้ง",
+ "action.stretch-vertical.short": "ยืดแนวตั้ง",
+ "action.toggle-auto-size": "สลับขนาดอัตโนมัติ",
+ "action.toggle-dark-mode": "โหมดมืด",
+ "action.toggle-dark-mode.menu": "โหมดมืด",
+ "action.toggle-debug-mode": "โหมดดีบัก",
+ "action.toggle-debug-mode.menu": "โหมดดีบัก",
+ "action.toggle-focus-mode": "โหมดโฟกัส",
+ "action.toggle-focus-mode.menu": "โหมดโฟกัส",
+ "action.toggle-grid": "แสดงตาราง",
+ "action.toggle-grid.menu": "แสดงตาราง",
+ "action.toggle-snap-mode": "สลับสแนปตลอด",
+ "action.toggle-snap-mode.menu": "แสดงสแนปตลอด",
+ "action.toggle-tool-lock": "สลับล็อคเครื่องมือ",
+ "action.toggle-tool-lock.menu": "ล็อคเครื่องมือ",
+ "action.toggle-transparent": "สลับพื้นหลังโปร่งใส",
+ "action.toggle-transparent.context-menu": "โปร่งใส",
+ "action.toggle-transparent.menu": "โปร่งใส",
+ "action.undo": "เลิกทำ",
+ "action.ungroup": "ยกเลิกจัดกลุ่ม",
+ "action.zoom-in": "ขยาย",
+ "action.zoom-out": "ย่อ",
+ "action.zoom-to-100": "ขยายเป็น 100%",
+ "action.zoom-to-fit": "ขยายให้พอดี",
+ "action.zoom-to-selection": "ขยายให้พอดีกับสิ่งที่เลือก",
+ "actions-menu.title": "การกระทำ",
+ "align-style.end": "สิ้นสุด",
+ "align-style.justify": "ปรับ",
+ "align-style.middle": "กลาง",
+ "align-style.start": "เริ่มต้น",
+ "arrowheadEnd-style.arrow": "ลูกศร",
+ "arrowheadEnd-style.bar": "แถบ",
+ "arrowheadEnd-style.diamond": "สี่เหลี่ยมเพชร",
+ "arrowheadEnd-style.dot": "จุด",
+ "arrowheadEnd-style.inverted": "กลับด้าน",
+ "arrowheadEnd-style.none": "ไม่มี",
+ "arrowheadEnd-style.pipe": "ท่อ",
+ "arrowheadEnd-style.square": "สี่เหลี่ยมจัตุรัส",
+ "arrowheadEnd-style.triangle": "สามเหลี่ยม",
+ "arrowheadStart-style.arrow": "ลูกศร",
+ "arrowheadStart-style.bar": "แถบ",
+ "arrowheadStart-style.diamond": "สี่เหลี่ยมเพชร",
+ "arrowheadStart-style.dot": "จุด",
+ "arrowheadStart-style.inverted": "กลับด้าน",
+ "arrowheadStart-style.none": "ไม่มี",
+ "arrowheadStart-style.pipe": "ท่อ",
+ "arrowheadStart-style.square": "สี่เหลี่ยมจัตุรัส",
+ "arrowheadStart-style.triangle": "สามเหลี่ยม",
+ "color-style.black": "สีดำ",
+ "color-style.blue": "สีฟ้า",
+ "color-style.green": "สีเขียว",
+ "color-style.grey": "สีเทา",
+ "color-style.light-blue": "สีม่วงอ่อน",
+ "color-style.light-green": "สีม่วงอ่อน",
+ "color-style.light-red": "สีม่วงอ่อน",
+ "color-style.light-violet": "สีม่วงอ่อน",
+ "color-style.orange": "สีส้ม",
+ "color-style.red": "สีแดง",
+ "color-style.violet": "สีม่วง",
+ "color-style.yellow": "สีเหลือง",
+ "context-menu.arrange": "จัดระเบียบ",
+ "context-menu.copy-as": "คัดลอกเป็น",
+ "context-menu.export-as": "ส่งออกเป็น",
+ "context-menu.move-to-page": "ย้ายไปยังหน้า",
+ "context-menu.reorder": "จัดเรียงลำดับ",
+ "context.pages.new-page": "หน้าใหม่",
+ "dash-style.dashed": "ประ",
+ "dash-style.dotted": "จุด",
+ "dash-style.draw": "ดินสอ",
+ "dash-style.solid": "หนาแข็ง",
+ "edit-link-dialog.cancel": "ยกเลิก",
+ "edit-link-dialog.clear": "ล้าง",
+ "edit-link-dialog.detail": "ลิงก์จะเปิดขึ้นในแท็บใหม่",
+ "edit-link-dialog.invalid-url": "ลิงก์ต้องเป็น URL ที่ถูกต้อง",
+ "edit-link-dialog.save": "ดำเนินการต่อ",
+ "edit-link-dialog.title": "แก้ไขลิงค์",
+ "edit-link-dialog.url": "URL",
+ "edit-pages-dialog.move-down": "เลื่อนลง",
+ "edit-pages-dialog.move-up": "เลื่อนขึ้น",
+ "embed-dialog.back": "กลับ",
+ "embed-dialog.cancel": "ยกเลิก",
+ "embed-dialog.create": "สร้าง",
+ "embed-dialog.instruction": "วาง URL ของไซต์เพื่อสร้าง embed",
+ "embed-dialog.invalid-url": "เราไม่สามารถสร้าง embed จาก URL นั้นได้",
+ "embed-dialog.title": "สร้าง embed",
+ "embed-dialog.url": "URL",
+ "file-system.confirm-open.cancel": "ยกเลิก",
+ "file-system.confirm-open.description": "การเปิดไฟล์จะแทนที่โครงการปัจจุบันของคุณและการเปลี่ยนแปลงที่ไม่ได้บันทึกจะหายไป คุณแน่ใจหรือไม่ว่าต้องการดําเนินการต่อ",
+ "file-system.confirm-open.dont-show-again": "ไม่ต้องถามอีก",
+ "file-system.confirm-open.open": "เปิดไฟล์",
+ "file-system.confirm-open.title": "เขียนทับโครงการปัจจุบันใช่หรือไม่",
+ "file-system.file-open-error.file-format-version-too-new": "ไฟล์ที่คุณพยายามเปิดมาจาก tldraw รุ่นใหม่กว่า โปรดโหลดหน้าเว็บและลองอีกครั้ง",
+ "file-system.file-open-error.generic-corrupted-file": "ไฟล์ที่คุณพยายามเปิดเสียหาย",
+ "file-system.file-open-error.not-a-tldraw-file": "ไฟล์ที่คุณพยายามเปิดดูไม่เหมือนไฟล์ tldraw",
+ "file-system.file-open-error.title": "ไม่สามารถเปิดไฟล์ได้",
+ "file-system.shared-document-file-open-error.description": "ไม่รองรับการเปิดไฟล์จากโครงการที่ใช้งานร่วมกัน",
+ "file-system.shared-document-file-open-error.title": "ไม่สามารถเปิดไฟล์ได้",
+ "fill-style.none": "ไม่มี",
+ "fill-style.pattern": "ลวดลาย",
+ "fill-style.semi": "กึ่ง",
+ "fill-style.solid": "หนาแข็ง",
+ "focus-mode.toggle-focus-mode": "โหมดโฟกัส",
+ "font-style.draw": "ดินสอ",
+ "font-style.mono": "โมโน",
+ "font-style.sans": "แซนส์",
+ "font-style.serif": "เซริฟ",
+ "geo-style.arrow-down": "ลูกศรลง",
+ "geo-style.arrow-left": "ลูกศรซ้าย",
+ "geo-style.arrow-right": "ลูกศรขวา",
+ "geo-style.arrow-up": "ลูกศรขึ้น",
+ "geo-style.diamond": "สี่เหลี่ยมเพชร",
+ "geo-style.ellipse": "วงรี",
+ "geo-style.hexagon": "หกเหลี่ยม",
+ "geo-style.octagon": "แปดเหลี่ยม",
+ "geo-style.oval": "วงรี",
+ "geo-style.pentagon": "ห้าเหลี่ยม",
+ "geo-style.rectangle": "สี่เหลี่ยมผืนผ้า",
+ "geo-style.rhombus": "สี่เหลี่ยมขนมเปียกปูน",
+ "geo-style.rhombus-2": "สี่เหลี่ยมขนมเปียกปูน",
+ "geo-style.star": "ดาว",
+ "geo-style.trapezoid": "สี่เหลี่ยมคางหมู",
+ "geo-style.triangle": "สามเหลี่ยม",
+ "geo-style.x-box": "สี่เหลี่ยมพื้นผ้า X",
+ "help-menu.about": "เกี่ยวกับ",
+ "help-menu.discord": "ดิสคอร์ด",
+ "help-menu.github": "กิตฮับ",
+ "help-menu.keyboard-shortcuts": "แป้นพิมพ์ลัด",
+ "help-menu.title": "ความช่วยเหลือและแหล่งข้อมูล",
+ "help-menu.twitter": "ทวิตเตอร์",
+ "menu.copy-as": "คัดลอกเป็น",
+ "menu.edit": "แก้ไข",
+ "menu.export-as": "ส่งออกเป็น",
+ "menu.file": "ไฟล์",
+ "menu.language": "ภาษา",
+ "menu.preferences": "ตั้งค่า",
+ "menu.title": "เมนู",
+ "menu.view": "มุมมอง",
+ "navigation-zone.toggle-minimap": "สลับแผนที่ย่อ",
+ "navigation-zone.zoom": "ขยาย",
+ "opacity-style.0.1": "๑๐%",
+ "opacity-style.0.25": "๒๕%",
+ "opacity-style.0.5": "๕๐%",
+ "opacity-style.0.75": "๗๕%",
+ "opacity-style.1": "๑๐๐%",
+ "page-menu.create-new-page": "สร้างหน้า",
+ "page-menu.edit-done": "เสร็จแล้ว",
+ "page-menu.edit-start": "แก้ไข",
+ "page-menu.max-page-count-reached": "จํานวนหน้าสูงสุดแล้ว",
+ "page-menu.new-page-initial-name": "หน้า 1",
+ "page-menu.submenu.delete": "ลบ",
+ "page-menu.submenu.duplicate-page": "สำเนา",
+ "page-menu.submenu.move-down": "เลื่อนลง",
+ "page-menu.submenu.move-up": "เลื่อนขึ้น",
+ "page-menu.submenu.rename": "เปลี่ยนชื่อ",
+ "page-menu.submenu.title": "เมนู",
+ "page-menu.title": "หน้า",
+ "people-menu.change-color": "เปลี่ยนสี",
+ "people-menu.change-name": "เปลี่ยนชื่อ",
+ "people-menu.invite": "เชิญผู้อื่น",
+ "people-menu.title": "ผู้คน",
+ "people-menu.user": "(คุณ)",
+ "share-menu.copy-link": "คัดลอกลิงก์เชิญ",
+ "share-menu.copy-link-note": "ทุกคนที่มีลิงก์จะสามารถดูและแก้ไขโครงการนี้ได้",
+ "share-menu.copy-readonly-link": "คัดลอกลิงก์แบบให้อ่านอย่างเดียว",
+ "share-menu.copy-readonly-link-note": "ทุกคนที่มีลิงก์จะสามารถดู (แต่ไม่สามารถแก้ไข) โครงการนี้ได้",
+ "share-menu.offline-note": "การแชร์โครงการต์นี้จะสร้างสําเนาสดที่โฮสต์ที่ URL ใหม่ คุณสามารถแชร์ URL กับบุคคลอื่นได้สูงสุดสามสิบคนเพื่อดูและแก้ไขโครงการด้วยกัน",
+ "share-menu.project-too-large": "ขออภัย ไม่สามารถแชร์โครงการนี้ได้เนื่องจากมีขนาดใหญ่เกินไป เรากำลังดำเนินการอยู่!",
+ "share-menu.readonly-link": "อ่านอย่างเดียว",
+ "share-menu.share-project": "แบ่งปันโครงการนี้",
+ "share-menu.title": "แบ่งปัน",
+ "shortcuts-dialog.edit": "แก้ไข",
+ "shortcuts-dialog.file": "ไฟล์",
+ "shortcuts-dialog.preferences": "ตั้งค่า",
+ "shortcuts-dialog.title": "แป้นพิมพ์ลัด",
+ "shortcuts-dialog.tools": "เครื่องมือ",
+ "shortcuts-dialog.transform": "แปลง",
+ "shortcuts-dialog.view": "มุมมอง",
+ "size-style.l": "ใหญ่",
+ "size-style.m": "ปานกลาง",
+ "size-style.s": "เล็ก",
+ "size-style.xl": "ขนาดใหญ่พิเศษ",
+ "spline-style.cubic": "ลูกบาศก์",
+ "spline-style.line": "เส้น",
+ "style-panel.align": "จัดแนว",
+ "style-panel.arrowheads": "หัวลูกศร",
+ "style-panel.color": "สี",
+ "style-panel.dash": "เส้นขีด",
+ "style-panel.fill": "เติม",
+ "style-panel.font": "แบบอักษร",
+ "style-panel.geo": "รูปร่าง",
+ "style-panel.mixed": "ผสม",
+ "style-panel.opacity": "ความทึบ",
+ "style-panel.size": "ขนาด",
+ "style-panel.spline": "เส้นโค้ง",
+ "style-panel.title": "รูปแบบ",
+ "toast.close": "ปิด",
+ "toast.error.copy-fail.desc": "คัดลอกรูปภาพไม่สําเร็จ",
+ "toast.error.copy-fail.title": "การคัดลอกล้มเหลว",
+ "toast.error.export-fail.desc": "การส่งออกรูปภาพไม่สำเร็จ",
+ "toast.error.export-fail.title": "การส่งออกล้มเหลว",
+ "tool-panel.drawing": "การวาดภาพ",
+ "tool-panel.shapes": "รูปทรง",
+ "tool.arrow": "ลูกศร",
+ "tool.arrow-down": "ลูกศรลง",
+ "tool.arrow-left": "ลูกศรซ้าย",
+ "tool.arrow-right": "ลูกศรขวา",
+ "tool.arrow-up": "ลูกศรขึ้น",
+ "tool.asset": "มีเดีย",
+ "tool.diamond": "สี่เหลี่ยมเพชร",
+ "tool.draw": "ดินสอ",
+ "tool.ellipse": "วงรี",
+ "tool.embed": "Embed",
+ "tool.eraser": "ยางลบ",
+ "tool.frame": "กรอบ",
+ "tool.hand": "มือ",
+ "tool.hexagon": "หกเหลี่ยม",
+ "tool.line": "เส้น",
+ "tool.note": "บันทึกย่อ",
+ "tool.octagon": "แปดเหลี่ยม",
+ "tool.oval": "วงรี",
+ "tool.pentagon": "ห้าเหลี่ยม",
+ "tool.rectangle": "สี่เหลี่ยมผืนผ้า",
+ "tool.rhombus": "สี่เหลี่ยมขนมเปียกปูน",
+ "tool.select": "เลือก",
+ "tool.star": "ดาว",
+ "tool.text": "กล่องข้อความ",
+ "tool.trapezoid": "สี่เหลี่ยมคางหมู",
+ "tool.triangle": "สามเหลี่ยม",
+ "tool.x-box": "สี่เหลี่ยมพื้นผ้า X",
+ "vscode.file-open.desc": "ไฟล์นี้ถูกสร้างขึ้นด้วย tldraw รุ่นก่อนหน้า คุณต้องการอัปเดตเพื่อทํางานกับรุ่นใหม่หรือไม่",
+ "vscode.file-open.dont-show-again": "ไม่ต้องถามอีก"
+}
diff --git a/apps/web/public/translations/tr.json b/apps/web/public/translations/tr.json
new file mode 100644
index 00000000..63b4a6bc
--- /dev/null
+++ b/apps/web/public/translations/tr.json
@@ -0,0 +1,355 @@
+{
+ "action.align-bottom": "Aşağı hizala",
+ "action.align-center-horizontal": "Yatay olanak hizala",
+ "action.align-center-horizontal.short": "Yatay hizala",
+ "action.align-center-vertical": "Dikey olarak hizala",
+ "action.align-center-vertical.short": "Dikey hizala",
+ "action.align-left": "Sola hizala",
+ "action.align-right": "Sağa hizala",
+ "action.align-top": "Yukarı hizala",
+ "action.back-to-content": "İçeriğe geri dön",
+ "action.bring-forward": "Öne doğru getir",
+ "action.bring-to-front": "Öne getir",
+ "action.convert-to-bookmark": "Yer işaretine dönüştür",
+ "action.convert-to-embed": "Yerleştirmeye dönüştür",
+ "action.copy": "Kopyala",
+ "action.copy-as-json": "JSON olarak kopyala",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "PNG olarak kopyala",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "SVG olarak kopyala",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "Kes",
+ "action.delete": "Sil",
+ "action.distribute-horizontal": "Yatay olarak dağıt",
+ "action.distribute-horizontal.short": "Yatay dağıt",
+ "action.distribute-vertical": "Dikey olarak dağıt",
+ "action.distribute-vertical.short": "Dikey dağıt",
+ "action.duplicate": "Kopya oluştur",
+ "action.edit-link": "Bağlantıyı düzenle",
+ "action.exit-pen-mode": "Kalem modundan çık",
+ "action.export-as-json": "JSON olarak dışa aktar",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "PNG olarak dışa aktar",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "SVG olarak dışa aktar",
+ "action.export-as-svg.short": "SVG",
+ "action.fit-frame-to-content": "İçeriği Hizala",
+ "action.flip-horizontal": "Yatay Çevir",
+ "action.flip-horizontal.short": "Yatay döndür",
+ "action.flip-vertical": "Dikey Çevir",
+ "action.flip-vertical.short": "Dikey döndür",
+ "action.fork-project": "Bu projeyi forkla",
+ "action.group": "Grupla",
+ "action.insert-embed": "Dışardan çağır",
+ "action.insert-media": "Medya Yükle",
+ "action.leave-shared-project": "Paylaşılan Projeyi Kapat",
+ "action.new-project": "Yeni Proje",
+ "action.new-shared-project": "Yeni ortak proje",
+ "action.open-cursor-chat": "Sohbet imleci",
+ "action.open-embed-link": "Bağlantıyı aç",
+ "action.open-file": "Dosya aç",
+ "action.pack": "Paket",
+ "action.paste": "Yapıştır",
+ "action.print": "Yazdır",
+ "action.redo": "Yinele",
+ "action.remove-frame": "Çerçeveyi kaldır",
+ "action.rotate-ccw": "Saat yönünün aksine döndür",
+ "action.rotate-cw": "Saat yönünde döndür",
+ "action.save-copy": "Bir kopyasını kaydet",
+ "action.select-all": "Hepsini Seç",
+ "action.select-none": "Hiçbirini Seçme",
+ "action.send-backward": "Arkaya doğru gönder",
+ "action.send-to-back": "Arkaya gönder",
+ "action.share-project": "Bu projeyi paylaş",
+ "action.stack-horizontal": "Yatay olarak istifle",
+ "action.stack-horizontal.short": "Yatay istifle",
+ "action.stack-vertical": "Dikey olarak istifle",
+ "action.stack-vertical.short": "Dikey istifle",
+ "action.stop-following": "Takibi bırak",
+ "action.stretch-horizontal": "Yatay olanak esnet",
+ "action.stretch-horizontal.short": "Yatay esnet",
+ "action.stretch-vertical": "Dikey olarak esnet",
+ "action.stretch-vertical.short": "Dikey esnet",
+ "action.toggle-auto-size": "Boyutu Uyarla aç/kapat",
+ "action.toggle-dark-mode": "Karanlık moda geçiş yap",
+ "action.toggle-dark-mode.menu": "Karanlık mod",
+ "action.toggle-debug-mode": "Hata ayıklama moduna geçiş yap",
+ "action.toggle-debug-mode.menu": "Hata ayıklama modu",
+ "action.toggle-edge-scrolling": "Kaydırma çubuğunu aç/kapat",
+ "action.toggle-edge-scrolling.menu": "Kaydırma çubuğu",
+ "action.toggle-focus-mode": "Odak moduna geçiş yap",
+ "action.toggle-focus-mode.menu": "Odak modu",
+ "action.toggle-grid": "Izgarayı değiştir",
+ "action.toggle-grid.menu": "Izgarayı göster",
+ "action.toggle-lock": "Kilidi Aç / Kilidi Kapat",
+ "action.toggle-reduce-motion": "Hareketi azaltmayı aç/kapat",
+ "action.toggle-reduce-motion.menu": "Hareketi azalt",
+ "action.toggle-snap-mode": "Her zaman tuttura geçiş yap",
+ "action.toggle-snap-mode.menu": "Her zaman tuttur",
+ "action.toggle-tool-lock": "Araç kilidine geçiş yap",
+ "action.toggle-tool-lock.menu": "Araç kilidi",
+ "action.toggle-transparent": "Şeffaf arkaplana geçiş yap",
+ "action.toggle-transparent.context-menu": "Şeffaf",
+ "action.toggle-transparent.menu": "Şeffaf",
+ "action.undo": "Geri Al",
+ "action.ungroup": "Gruplamayı Kaldır",
+ "action.unlock-all": "Tüm Kilitleri Aç",
+ "action.zoom-in": "Yakınlaştır",
+ "action.zoom-out": "Uzaklaştır",
+ "action.zoom-to-100": "%100 yakınlaştır",
+ "action.zoom-to-fit": "Sığdırmak için Yakınlaştır",
+ "action.zoom-to-selection": "Seçime Yakınlaştır",
+ "actions-menu.title": "Eylemler",
+ "align-style.end": "Son",
+ "align-style.justify": "Blokla",
+ "align-style.middle": "Orta",
+ "align-style.start": "Başla",
+ "arrowheadEnd-style.arrow": "Ok",
+ "arrowheadEnd-style.bar": "Çubuk",
+ "arrowheadEnd-style.diamond": "Karo",
+ "arrowheadEnd-style.dot": "Nokta",
+ "arrowheadEnd-style.inverted": "Ters",
+ "arrowheadEnd-style.none": "Hiçbiri",
+ "arrowheadEnd-style.pipe": "Boru",
+ "arrowheadEnd-style.square": "Kare",
+ "arrowheadEnd-style.triangle": "Üçgen",
+ "arrowheadStart-style.arrow": "Ok",
+ "arrowheadStart-style.bar": "Çubuk",
+ "arrowheadStart-style.diamond": "Karo",
+ "arrowheadStart-style.dot": "Nokta",
+ "arrowheadStart-style.inverted": "Ters",
+ "arrowheadStart-style.none": "Hiçbiri",
+ "arrowheadStart-style.pipe": "Boru",
+ "arrowheadStart-style.square": "Kare",
+ "arrowheadStart-style.triangle": "Üçgen",
+ "color-style.black": "Siyah",
+ "color-style.blue": "Mavi",
+ "color-style.green": "Yeşil",
+ "color-style.grey": "Gri",
+ "color-style.light-blue": "Açık mavi",
+ "color-style.light-green": "Açık yeşil",
+ "color-style.light-red": "Açık kırmızı",
+ "color-style.light-violet": "Açık eflatun",
+ "color-style.orange": "Turuncu",
+ "color-style.red": "Kırmızı",
+ "color-style.violet": "Eflatun",
+ "color-style.yellow": "Sarı",
+ "context-menu.arrange": "Düzenle",
+ "context-menu.copy-as": "Olarak Kopyala",
+ "context-menu.export-as": "Olarak Dışarı Aktar",
+ "context-menu.move-to-page": "Sayfaya Taşı",
+ "context-menu.reorder": "Yeniden sırala",
+ "context.pages.new-page": "Yeni sayfa",
+ "cursor-chat.type-to-chat": "Bir şeyler yaz...",
+ "dash-style.dashed": "Kesik çizgili",
+ "dash-style.dotted": "Noktalı",
+ "dash-style.draw": "Çizim",
+ "dash-style.solid": "Düz",
+ "debug-panel.more": "Daha Fazla",
+ "edit-link-dialog.cancel": "İptal",
+ "edit-link-dialog.clear": "Temizle",
+ "edit-link-dialog.detail": "Bağlantılar yeni sekmede açılacaktır.",
+ "edit-link-dialog.invalid-url": "Bağlantı geçerli bir URL olmalıdır.",
+ "edit-link-dialog.save": "Devam et",
+ "edit-link-dialog.title": "Bağlantıyı düzenle",
+ "edit-link-dialog.url": "URL",
+ "edit-pages-dialog.move-down": "Aşağı taşı",
+ "edit-pages-dialog.move-up": "Yukarı taşı",
+ "embed-dialog.back": "Geri",
+ "embed-dialog.cancel": "İptal",
+ "embed-dialog.create": "Oluştur",
+ "embed-dialog.instruction": "Yerleştirmeyi oluşturmak için sitenin URL adresini yapıştırın.",
+ "embed-dialog.invalid-url": "Bu URL'den yerleştirme oluşturamadık.",
+ "embed-dialog.title": "Yerleştirme oluştur",
+ "embed-dialog.url": "URL",
+ "file-system.confirm-clear.cancel": "Vazgeç",
+ "file-system.confirm-clear.continue": "Devam",
+ "file-system.confirm-clear.description": "Yeni proje başlatmak, mevcut projedeki herşeyi siler. Devam etmek istiyor musun?",
+ "file-system.confirm-clear.dont-show-again": "Bir daha sorma",
+ "file-system.confirm-clear.title": "Mevcut projeyi temizle",
+ "file-system.confirm-open.cancel": "İptal",
+ "file-system.confirm-open.description": "Açtığınız yeni dosya mevcut projenizin yerini alacak ve kaydedilmemiş tüm değişiklikler kaybolacaktır. Devam etmek istediğinize emin misiniz?",
+ "file-system.confirm-open.dont-show-again": "Bir daha sorma",
+ "file-system.confirm-open.open": "Dosya Aç",
+ "file-system.confirm-open.title": "Mevcut projenin üzerine yazılsın mı?",
+ "file-system.file-open-error.file-format-version-too-new": "Açmaya çalıştığınız dosya daha yeni bir tldraw versiyonuna ait. Lütfen sayfayı yenileyip tekrar deneyin.",
+ "file-system.file-open-error.generic-corrupted-file": "Açmaya çalıştığınız dosya bozuk",
+ "file-system.file-open-error.not-a-tldraw-file": "Açmaya çalıştığınız dosya tldraw dosyasına benzemiyor.",
+ "file-system.file-open-error.title": "Dosya açılamadı",
+ "file-system.shared-document-file-open-error.description": "Paylaşılan projelerdeki dosyaların açılmaası desteklenmemektedir.",
+ "file-system.shared-document-file-open-error.title": "Dosya açılamadı",
+ "fill-style.none": "Hiçbiri",
+ "fill-style.pattern": "Desen",
+ "fill-style.semi": "Yarı",
+ "fill-style.solid": "Düz",
+ "focus-mode.toggle-focus-mode": "Odak modunu değiştir",
+ "font-style.draw": "Çizim",
+ "font-style.mono": "Mono",
+ "font-style.sans": "Sans",
+ "font-style.serif": "Serif",
+ "geo-style.arrow-down": "Aşağı ok",
+ "geo-style.arrow-left": "Sol ok",
+ "geo-style.arrow-right": "Sağ ok",
+ "geo-style.arrow-up": "Yukarı ok",
+ "geo-style.check-box": "Onay Kutusu",
+ "geo-style.cloud": "Bulut",
+ "geo-style.diamond": "Karo",
+ "geo-style.ellipse": "Elips",
+ "geo-style.hexagon": "Altıgen",
+ "geo-style.octagon": "Sekizgen",
+ "geo-style.oval": "Oval",
+ "geo-style.pentagon": "Beşgen",
+ "geo-style.rectangle": "Dikdörtgen",
+ "geo-style.rhombus": "Eşkenar dörtgen",
+ "geo-style.rhombus-2": "Eşkenar Dörtgen 2",
+ "geo-style.star": "Yıldız",
+ "geo-style.trapezoid": "Yamuk",
+ "geo-style.triangle": "Üçgen",
+ "geo-style.x-box": "X Kutusu",
+ "help-menu.about": "Hakkında",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "Github",
+ "help-menu.keyboard-shortcuts": "Klavye kısayolları",
+ "help-menu.title": "Yardım ve kaynaklar",
+ "help-menu.twitter": "Twitter",
+ "home-project-dialog.description": "Bu sizin yerel ana projenizdir. Sadece sizin için.",
+ "home-project-dialog.ok": "Tamam",
+ "home-project-dialog.title": "Ana Proje",
+ "menu.copy-as": "Olarak Kopyala",
+ "menu.edit": "Düzenle",
+ "menu.export-as": "Olarak Dışarı Aktar",
+ "menu.file": "Dosya",
+ "menu.language": "Dil",
+ "menu.preferences": "Tercihler",
+ "menu.title": "Menü",
+ "menu.view": "Görüntü",
+ "navigation-zone.toggle-minimap": "Mini haritayı aç/kapat",
+ "navigation-zone.zoom": "Yakınlaştır",
+ "opacity-style.0.1": "%10",
+ "opacity-style.0.25": "%25",
+ "opacity-style.0.5": "%50",
+ "opacity-style.0.75": "%75",
+ "opacity-style.1": "%100",
+ "page-menu.create-new-page": "Yeni sayfa oluştur",
+ "page-menu.edit-done": "Bitti",
+ "page-menu.edit-start": "Düzenle",
+ "page-menu.go-to-page": "Sayfaya git",
+ "page-menu.max-page-count-reached": "Maksimum sayfaya ulaşıldı.",
+ "page-menu.new-page-initial-name": "Sayfa 1",
+ "page-menu.submenu.delete": "Sil",
+ "page-menu.submenu.duplicate-page": "Kopya oluştur",
+ "page-menu.submenu.move-down": "Aşağı taşı",
+ "page-menu.submenu.move-up": "Yukarı taşı",
+ "page-menu.submenu.rename": "Yeniden adlandır",
+ "page-menu.submenu.title": "Menü",
+ "page-menu.title": "Sayfalar",
+ "people-menu.change-color": "Rengi değiştir",
+ "people-menu.change-name": "İsmi değiştir",
+ "people-menu.follow": "Takip et",
+ "people-menu.following": "Takip ediliyor",
+ "people-menu.invite": "Başkalarını davet et",
+ "people-menu.leading": "Seni takip ediyor",
+ "people-menu.title": "Kişiler",
+ "people-menu.user": "(Siz)",
+ "rename-project-dialog.cancel": "Vazgeç",
+ "rename-project-dialog.rename": "Yeniden Adlandır",
+ "rename-project-dialog.title": "Projeyi yeniden adlandır",
+ "share-menu.copy-link": "Bağlantıyı kopyala",
+ "share-menu.copy-link-note": "Bağlantıya sahip olan herkes bu projeyi görüntüleyebilir ve düzenleyebilir.",
+ "share-menu.copy-readonly-link": "Salt okunur bağlantıyı kopyala",
+ "share-menu.copy-readonly-link-note": "Bağlantıya sahip olan herkes bu projeyi görüntüleyebilir (ancak düzenleyemez)",
+ "share-menu.create-snapshot-link": "Link ile dışarıya ver",
+ "share-menu.default-project-name": "Projeyi Paylaş",
+ "share-menu.fork-note": "Linki üzerinden projeyi içe aktar",
+ "share-menu.offline-note": "Bu projeyi paylaşmak, yeni bir URL'de barındırılan canlı bir kopya oluşturacaktır. Projeyi birlikte görüntülemek ve düzenlemek için bağlantıyı en fazla otuz kişiyle paylaşabilirsiniz.",
+ "share-menu.project-too-large": "Üzgünüz, bu proje çok büyük olduğu için paylaşılamıyor. Üzerinde çalışıyoruz!",
+ "share-menu.readonly-link": "Salt okunur",
+ "share-menu.save-note": "Projeyi .tldr olarak dışa aktar",
+ "share-menu.share-project": "Bu projeyi paylaş",
+ "share-menu.snapshot-link-note": "Link ile sadece görüntülenebilir şekilde dışarıya ver",
+ "share-menu.title": "Paylaş",
+ "share-menu.upload-failed": "Proje yüklenirken hata oluştu, lütfen daha sonra tekrar deneyin.",
+ "sharing.confirm-leave.cancel": "Vazgeç",
+ "sharing.confirm-leave.description": "Paylaşılan projeden ayrılamak istediğinize emin misiniz?. Daha sonra aynı link ile geri dönebilirsiniz.",
+ "sharing.confirm-leave.dont-show-again": "Bir daha sorma",
+ "sharing.confirm-leave.leave": "Ayrıl",
+ "sharing.confirm-leave.title": "Açık Projeyi Kapat",
+ "shortcuts-dialog.collaboration": "İşbirliği",
+ "shortcuts-dialog.edit": "Düzenle",
+ "shortcuts-dialog.file": "Dosya",
+ "shortcuts-dialog.preferences": "Tercihler",
+ "shortcuts-dialog.title": "Klavye kısayolları",
+ "shortcuts-dialog.tools": "Araçlar",
+ "shortcuts-dialog.transform": "Dönüştür",
+ "shortcuts-dialog.view": "Görüntü",
+ "size-style.l": "Büyük",
+ "size-style.m": "Orta boy",
+ "size-style.s": "Küçük",
+ "size-style.xl": "Ekstra büyük",
+ "spline-style.cubic": "Kübik",
+ "spline-style.line": "Çizgi",
+ "status.offline": "Çevrimdışı",
+ "status.online": "Çevrimiçi",
+ "style-panel.align": "Hizala",
+ "style-panel.arrowhead-end": "Bitiş",
+ "style-panel.arrowhead-start": "Başlangıç",
+ "style-panel.arrowheads": "Ok uçları",
+ "style-panel.color": "Renk",
+ "style-panel.dash": "Çizgi",
+ "style-panel.fill": "Doldur",
+ "style-panel.font": "Yazı Tipi",
+ "style-panel.geo": "Şekil",
+ "style-panel.mixed": "Karışık",
+ "style-panel.opacity": "Opaklık",
+ "style-panel.position": "Pozisyon",
+ "style-panel.size": "Boyut",
+ "style-panel.spline": "Eğri",
+ "style-panel.title": "Stiller",
+ "style-panel.vertical-align": "Dikey Hizalama",
+ "toast.close": "Kapat",
+ "toast.error.copy-fail.desc": "Görsel kopyalanamadı",
+ "toast.error.copy-fail.title": "Kopyalama başarısız",
+ "toast.error.export-fail.desc": "Görsel dışa aktarılamadı",
+ "toast.error.export-fail.title": "Dışa aktarma başarısız",
+ "tool-panel.drawing": "Çizim",
+ "tool-panel.more": "Daha Fazla",
+ "tool-panel.shapes": "Şekiller",
+ "tool.arrow": "Ok",
+ "tool.arrow-down": "Aşağı ok",
+ "tool.arrow-left": "Sol ok",
+ "tool.arrow-right": "Sağ ok",
+ "tool.arrow-up": "Yukarı ok",
+ "tool.asset": "Varlık",
+ "tool.check-box": "Onay Kutusu",
+ "tool.cloud": "Bulut",
+ "tool.diamond": "Karo",
+ "tool.draw": "Çizim",
+ "tool.ellipse": "Elips",
+ "tool.embed": "Yerleştir",
+ "tool.eraser": "Silgi",
+ "tool.frame": "Çerçeve",
+ "tool.hand": "El",
+ "tool.hexagon": "Altıgen",
+ "tool.highlight": "Vurgu",
+ "tool.laser": "Lazer",
+ "tool.line": "Çizgi",
+ "tool.note": "Yapışkan",
+ "tool.octagon": "Sekizgen",
+ "tool.oval": "Oval",
+ "tool.pentagon": "Beşgen",
+ "tool.rectangle": "Dikdörtgen",
+ "tool.rhombus": "Eşkenar dörtgen",
+ "tool.select": "Seç",
+ "tool.star": "Yıldız",
+ "tool.text": "Yazı",
+ "tool.trapezoid": "Yamuk",
+ "tool.triangle": "Üçgen",
+ "tool.x-box": "X kutusu",
+ "vscode.file-open.backup": "Yedek",
+ "vscode.file-open.backup-failed": "Yedekleme başarısız: bu .tldr uzantısına sahip bir dosya değil.",
+ "vscode.file-open.backup-saved": "Yedek Kaydedildi",
+ "vscode.file-open.desc": "Bu dosya daha önceki bir tldraw versiyonu ile oluşturulmuştur. Yeni versiyonla çalışmak için güncellemek ister misiniz?",
+ "vscode.file-open.dont-show-again": "Bir daha sorma",
+ "vscode.file-open.open": "Devam"
+}
diff --git a/apps/web/public/translations/uk.json b/apps/web/public/translations/uk.json
new file mode 100644
index 00000000..bdeeb8b7
--- /dev/null
+++ b/apps/web/public/translations/uk.json
@@ -0,0 +1,347 @@
+{
+ "action.align-bottom": "Вирівняти за нижнім краєм",
+ "action.align-center-horizontal": "Вирівняти по горизонталі",
+ "action.align-center-horizontal.short": "Вирівняти по горизонталі",
+ "action.align-center-vertical": "Вирівняти по вертикалі",
+ "action.align-center-vertical.short": "Вирівняти по вертикалі",
+ "action.align-left": "Вирівняти за лівим краєм",
+ "action.align-right": "Вирівняти за правим краєм",
+ "action.align-top": "Вирівняти за верхнім краєм",
+ "action.back-to-content": "Повернутися до змісту",
+ "action.bring-forward": "Перемістити вперед",
+ "action.bring-to-front": "На передній план",
+ "action.convert-to-bookmark": "Перетворити у закладку",
+ "action.convert-to-embed": "Конвертувати у вбудовування",
+ "action.copy": "Копіювати",
+ "action.copy-as-json": "Копіювати як JSON",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "Копіювати як PNG",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "Копіювати як SVG",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "Вирізати",
+ "action.delete": "Видалити",
+ "action.distribute-horizontal": "Розподілити по горизонталі",
+ "action.distribute-horizontal.short": "Розподілити по горизонталі",
+ "action.distribute-vertical": "Розподілити по вертикалі",
+ "action.distribute-vertical.short": "Розподілити по вертикалі",
+ "action.duplicate": "Дублювати",
+ "action.edit-link": "Редагувати посилання",
+ "action.exit-pen-mode": "Вийти з режиму пера",
+ "action.export-as-json": "Експортувати як JSON",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "Експортувати як PNG",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "Експортувати як SVG",
+ "action.export-as-svg.short": "SVG",
+ "action.flip-horizontal": "Віддзеркалити по горизонталі",
+ "action.flip-horizontal.short": "Віддзеркалити по горизонталі",
+ "action.flip-vertical": "Віддзеркалити по вертикалі",
+ "action.flip-vertical.short": "Віддзеркалити по вертикалі",
+ "action.fork-project": "Скопіювати цей проєкт",
+ "action.group": "Згрупувати",
+ "action.insert-embed": "Вставити вбудовування",
+ "action.insert-media": "Завантажити медіа",
+ "action.leave-shared-project": "Вийти зі спільного проєкту",
+ "action.new-project": "Новий проєкт",
+ "action.new-shared-project": "Новий спільний проєкт",
+ "action.open-embed-link": "Відкрити посилання",
+ "action.open-file": "Відкрити файл",
+ "action.pack": "Зібрати до купи",
+ "action.paste": "Вставити",
+ "action.print": "Друк",
+ "action.redo": "Повторити",
+ "action.rotate-ccw": "Обернути проти годинникової стрілки",
+ "action.rotate-cw": "Обернути за годинниковою стрілкою",
+ "action.save-copy": "Зберегти копію",
+ "action.select-all": "Обрати все",
+ "action.select-none": "Зняти виділення",
+ "action.send-backward": "Перемістити назад",
+ "action.send-to-back": "На задній план",
+ "action.share-project": "Поділитися цим проєктом",
+ "action.stack-horizontal": "Розмістити горизонтально",
+ "action.stack-horizontal.short": "Розмістити горизонтально",
+ "action.stack-vertical": "Розмістити вертикально",
+ "action.stack-vertical.short": "Розмістити вертикально",
+ "action.stop-following": "Припинити стежити",
+ "action.stretch-horizontal": "Розтягнути по горизонталі",
+ "action.stretch-horizontal.short": "Розтягнути по горизонталі",
+ "action.stretch-vertical": "Розтягнути по вертикалі",
+ "action.stretch-vertical.short": "Розтягнути по вертикалі",
+ "action.toggle-auto-size": "Перемкнути автоматичний розмір",
+ "action.toggle-dark-mode": "Перемкнути темний режим",
+ "action.toggle-dark-mode.menu": "Темний режим",
+ "action.toggle-debug-mode": "Перемкнути режим налагодження",
+ "action.toggle-debug-mode.menu": "Режим налагодження",
+ "action.toggle-focus-mode": "Перемкнути режим концентрації",
+ "action.toggle-focus-mode.menu": "Режим концентрації",
+ "action.toggle-grid": "Перемкнути сітку",
+ "action.toggle-grid.menu": "Показати сітку",
+ "action.toggle-lock": "Блокувати / Розблокувати",
+ "action.toggle-snap-mode": "Перемкнути постійне прив’язування",
+ "action.toggle-snap-mode.menu": "Завжди прив'язуватися",
+ "action.toggle-tool-lock": "Перемкнути блокування інструменту",
+ "action.toggle-tool-lock.menu": "Блокування інструменту",
+ "action.toggle-transparent": "Перемкнути прозорий фон",
+ "action.toggle-transparent.context-menu": "Прозорий",
+ "action.toggle-transparent.menu": "Прозорий",
+ "action.undo": "Скасувати",
+ "action.ungroup": "Розгрупувати",
+ "action.unlock-all": "Розблокувати все",
+ "action.zoom-in": "Збільшити",
+ "action.zoom-out": "Зменшити",
+ "action.zoom-to-100": "Масштабувати до 100%",
+ "action.zoom-to-fit": "Масштабувати до розміру вікна",
+ "action.zoom-to-selection": "Масштабувати до виділення",
+ "actions-menu.title": "Дії",
+ "align-style.end": "За правим краєм",
+ "align-style.justify": "За шириною",
+ "align-style.middle": "За центром",
+ "align-style.start": "За лівим краєм",
+ "arrowheadEnd-style.arrow": "Стрілка",
+ "arrowheadEnd-style.bar": "Лінія",
+ "arrowheadEnd-style.diamond": "Ромб",
+ "arrowheadEnd-style.dot": "Коло",
+ "arrowheadEnd-style.inverted": "Обернена",
+ "arrowheadEnd-style.none": "Без",
+ "arrowheadEnd-style.pipe": "Труба",
+ "arrowheadEnd-style.square": "Квадрат",
+ "arrowheadEnd-style.triangle": "Трикутник",
+ "arrowheadStart-style.arrow": "Стрілка",
+ "arrowheadStart-style.bar": "Лінія",
+ "arrowheadStart-style.diamond": "Ромб",
+ "arrowheadStart-style.dot": "Коло",
+ "arrowheadStart-style.inverted": "Обернена",
+ "arrowheadStart-style.none": "Без",
+ "arrowheadStart-style.pipe": "Труба",
+ "arrowheadStart-style.square": "Квадрат",
+ "arrowheadStart-style.triangle": "Трикутник",
+ "color-style.black": "Чорний",
+ "color-style.blue": "Синій",
+ "color-style.green": "Зелений",
+ "color-style.grey": "Сірий",
+ "color-style.light-blue": "Блакитний",
+ "color-style.light-green": "Світло-зелений",
+ "color-style.light-red": "Світло-червоний",
+ "color-style.light-violet": "Світло-фіолетовий",
+ "color-style.orange": "Помаранчевий",
+ "color-style.red": "Червоний",
+ "color-style.violet": "Фіолетовий",
+ "color-style.yellow": "Жовтий",
+ "context-menu.arrange": "Організувати",
+ "context-menu.copy-as": "Скопіювати як",
+ "context-menu.export-as": "Експортувати як",
+ "context-menu.move-to-page": "Перенести на сторінку",
+ "context-menu.reorder": "Перевпорядкувати",
+ "context.pages.new-page": "Нова сторінка",
+ "cursor-chat.type-to-chat": "Натисніть, щоб розпочати спілкуватися...",
+ "dash-style.dashed": "Штриховий",
+ "dash-style.dotted": "Пунктирний",
+ "dash-style.draw": "Художній",
+ "dash-style.solid": "Суцільний",
+ "debug-panel.more": "Детальніше",
+ "edit-link-dialog.cancel": "Скасувати",
+ "edit-link-dialog.clear": "Очистити",
+ "edit-link-dialog.detail": "Посилання відкриються в новій вкладці.",
+ "edit-link-dialog.invalid-url": "Посилання має бути дійсною URL-адресою.",
+ "edit-link-dialog.save": "Далі",
+ "edit-link-dialog.title": "Редагувати посилання",
+ "edit-link-dialog.url": "URL-адреса",
+ "edit-pages-dialog.move-down": "Перемістити вниз",
+ "edit-pages-dialog.move-up": "Перемістити вгору",
+ "embed-dialog.back": "Назад",
+ "embed-dialog.cancel": "Скасувати",
+ "embed-dialog.create": "Створити",
+ "embed-dialog.instruction": "Вставте URL-адресу сайту, щоб створити вбудовування.",
+ "embed-dialog.invalid-url": "Ми не змогли створити вбудовування з цієї URL-адреси.",
+ "embed-dialog.title": "Створити вбудовування",
+ "embed-dialog.url": "URL-адреса",
+ "file-system.confirm-clear.cancel": "Скасувати",
+ "file-system.confirm-clear.continue": "Далі",
+ "file-system.confirm-clear.description": "Створення нового проєкту очистить ваш поточний проєкт, і всі незбережені зміни будуть втрачені. Ви впевнені, що хочете продовжити?",
+ "file-system.confirm-clear.dont-show-again": "Більше не запитувати",
+ "file-system.confirm-clear.title": "Очистити поточний проєкт?",
+ "file-system.confirm-open.cancel": "Скасувати",
+ "file-system.confirm-open.description": "Відкриття файлу замінить ваш поточний проєкт, і будь-які незбережені зміни будуть втрачені. Ви дійсно бажаєте продовжити?",
+ "file-system.confirm-open.dont-show-again": "Не запитувати більше",
+ "file-system.confirm-open.open": "Відкрити файл",
+ "file-system.confirm-open.title": "Перезаписати поточний проєкт?",
+ "file-system.file-open-error.file-format-version-too-new": "Файл, який ви намагалися відкрити, належить до новішої версії tldraw. Будь ласка, перезавантажте сторінку і спробуйте ще раз.",
+ "file-system.file-open-error.generic-corrupted-file": "Файл, який ви намагалися відкрити, пошкоджено.",
+ "file-system.file-open-error.not-a-tldraw-file": "Файл, який ви намагалися відкрити, не схожий на файл tldraw.",
+ "file-system.file-open-error.title": "Не вдалося відкрити файл",
+ "file-system.shared-document-file-open-error.description": "Відкриття файлів зі спільних проєктів не підтримується.",
+ "file-system.shared-document-file-open-error.title": "Не вдалося відкрити файл",
+ "fill-style.none": "Без",
+ "fill-style.pattern": "Візерунок",
+ "fill-style.semi": "Напів",
+ "fill-style.solid": "Суцільне",
+ "focus-mode.toggle-focus-mode": "Перемкнути режим концентрації",
+ "font-style.draw": "Художній",
+ "font-style.mono": "Моноширинний",
+ "font-style.sans": "Без засічок",
+ "font-style.serif": "Із засічками",
+ "geo-style.arrow-down": "Стрілка вниз",
+ "geo-style.arrow-left": "Стрілка вліво",
+ "geo-style.arrow-right": "Стрілка вправо",
+ "geo-style.arrow-up": "Стрілка вгору",
+ "geo-style.check-box": "Прапорець",
+ "geo-style.cloud": "Хмара",
+ "geo-style.diamond": "Ромб",
+ "geo-style.ellipse": "Еліпс",
+ "geo-style.hexagon": "Шестикутник",
+ "geo-style.octagon": "Восьмикутник",
+ "geo-style.oval": "Овал",
+ "geo-style.pentagon": "П'ятикутник",
+ "geo-style.rectangle": "Прямокутник",
+ "geo-style.rhombus": "Ромб",
+ "geo-style.rhombus-2": "Ромб 2",
+ "geo-style.star": "Зірка",
+ "geo-style.trapezoid": "Трапеція",
+ "geo-style.triangle": "Трикутник",
+ "geo-style.x-box": "X квадрат",
+ "help-menu.about": "Про нас",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "Комбінації клавіш",
+ "help-menu.title": "Довідка та матеріали",
+ "help-menu.twitter": "Twitter",
+ "home-project-dialog.description": "Це ваш локальний домашній проєкт. Це тільки для вас!",
+ "home-project-dialog.ok": "Гаразд",
+ "home-project-dialog.title": "Домашній проєкт",
+ "menu.copy-as": "Скопіювати як",
+ "menu.edit": "Правка",
+ "menu.export-as": "Експортувати як",
+ "menu.file": "Файл",
+ "menu.language": "Мова",
+ "menu.preferences": "Налаштування",
+ "menu.title": "Меню",
+ "menu.view": "Вигляд",
+ "navigation-zone.toggle-minimap": "Перемкнути мінімапу",
+ "navigation-zone.zoom": "Збільшити",
+ "opacity-style.0.1": "10%",
+ "opacity-style.0.25": "25%",
+ "opacity-style.0.5": "50%",
+ "opacity-style.0.75": "75%",
+ "opacity-style.1": "100%",
+ "page-menu.create-new-page": "Створити нову сторінку",
+ "page-menu.edit-done": "Готово",
+ "page-menu.edit-start": "Редагувати",
+ "page-menu.go-to-page": "Перейти на сторінку",
+ "page-menu.max-page-count-reached": "Досягнуто максимальної кількості сторінок",
+ "page-menu.new-page-initial-name": "Сторінка 1",
+ "page-menu.submenu.delete": "Видалити",
+ "page-menu.submenu.duplicate-page": "Дублювати",
+ "page-menu.submenu.move-down": "Перемістити вниз",
+ "page-menu.submenu.move-up": "Перемістити вгору",
+ "page-menu.submenu.rename": "Перейменувати",
+ "page-menu.submenu.title": "Меню",
+ "page-menu.title": "Сторінки",
+ "people-menu.change-color": "Змінити колір",
+ "people-menu.change-name": "Змінити ім'я",
+ "people-menu.follow": "Стежити",
+ "people-menu.following": "Стежимо за",
+ "people-menu.invite": "Запросити інших користувачів",
+ "people-menu.leading": "Стежать за Вами",
+ "people-menu.title": "Люди",
+ "people-menu.user": "(Ви)",
+ "rename-project-dialog.cancel": "Скасувати",
+ "rename-project-dialog.rename": "Перейменувати",
+ "rename-project-dialog.title": "Перейменувати проєкт",
+ "share-menu.copy-link": "Копіювати посилання",
+ "share-menu.copy-link-note": "Будь-хто, маючи посилання, зможе переглянути та відредагувати цей проєкт.",
+ "share-menu.copy-readonly-link": "Скопіювати посилання, доступне лише для читання",
+ "share-menu.copy-readonly-link-note": "Будь-хто, маючи посилання, зможе переглянути (але не редагувати) цей проєкт.",
+ "share-menu.create-snapshot-link": "Створити посилання на знімок",
+ "share-menu.default-project-name": "Спільний проєкт",
+ "share-menu.fork-note": "Створіть новий спільний проєкт на основі цього знімка.",
+ "share-menu.offline-note": "Спільний доступ до цього проєкту створить розміщену онлайн копію за новою URL-адресою. Ви можете поділитися URL-адресою з тридцятьма іншими людьми, щоб вони мали змогу переглядати та редагувати проєкт разом з вами.",
+ "share-menu.project-too-large": "На жаль, цим проєктом не можна поділитися, оскільки він занадто великий. Ми працюємо над цим!",
+ "share-menu.readonly-link": "Лише для читання",
+ "share-menu.save-note": "Завантажте цей проєкт на свій комп’ютер як .tldr файл.",
+ "share-menu.share-project": "Поділитися цим проєктом",
+ "share-menu.snapshot-link-note": "Поділіться посиланням на цей проєкт в режимі лише для читання.",
+ "share-menu.title": "Поділитися",
+ "share-menu.upload-failed": "На жаль, наразі ми не можемо завантажити ваш проєкт. Спробуйте ще раз або повідомте нам, якщо проблема не зникне.",
+ "sharing.confirm-leave.cancel": "Скасувати",
+ "sharing.confirm-leave.description": "Ви впевнені, що бажаєте залишити цей спільний проєкт? Ви можете повернутися до нього, перейшовши за його URL-адресою.",
+ "sharing.confirm-leave.dont-show-again": "Більше не запитувати",
+ "sharing.confirm-leave.leave": "Вийти",
+ "sharing.confirm-leave.title": "Залишити поточний проєкт?",
+ "shortcuts-dialog.edit": "Правка",
+ "shortcuts-dialog.file": "Файл",
+ "shortcuts-dialog.preferences": "Налаштування",
+ "shortcuts-dialog.title": "Комбінації клавіш",
+ "shortcuts-dialog.tools": "Інструменти",
+ "shortcuts-dialog.transform": "Перетворення",
+ "shortcuts-dialog.view": "Вигляд",
+ "size-style.l": "Великий",
+ "size-style.m": "Середній",
+ "size-style.s": "Маленький",
+ "size-style.xl": "Дуже великий",
+ "spline-style.cubic": "Кубічний",
+ "spline-style.line": "Прямий",
+ "status.offline": "Офлайн",
+ "status.online": "Онлайн",
+ "style-panel.align": "Вирівняти",
+ "style-panel.arrowhead-end": "Хвіст",
+ "style-panel.arrowhead-start": "Наконечник",
+ "style-panel.arrowheads": "Стрілка",
+ "style-panel.color": "Колір",
+ "style-panel.dash": "Контур",
+ "style-panel.fill": "Заливка",
+ "style-panel.font": "Шрифт",
+ "style-panel.geo": "Форма",
+ "style-panel.mixed": "Змішаний",
+ "style-panel.opacity": "Непрозорість",
+ "style-panel.position": "Позиція",
+ "style-panel.size": "Розмір",
+ "style-panel.spline": "Сплайн",
+ "style-panel.title": "Стилі",
+ "style-panel.vertical-align": "Вертикальне вирівнювання",
+ "toast.close": "Закрити",
+ "toast.error.copy-fail.desc": "Не вдалося скопіювати зображення",
+ "toast.error.copy-fail.title": "Не вдалося скопіювати",
+ "toast.error.export-fail.desc": "Не вдалося експортувати зображення",
+ "toast.error.export-fail.title": "Помилка експорту",
+ "tool-panel.drawing": "Малюнок",
+ "tool-panel.more": "Детальніше",
+ "tool-panel.shapes": "Форми",
+ "tool.arrow": "Стрілка",
+ "tool.arrow-down": "Стрілка вниз",
+ "tool.arrow-left": "Стрілка вліво",
+ "tool.arrow-right": "Стрілка вправо",
+ "tool.arrow-up": "Стрілка вгору",
+ "tool.asset": "Ресурс",
+ "tool.check-box": "Прапорець",
+ "tool.cloud": "Хмара",
+ "tool.diamond": "Ромбоїд",
+ "tool.draw": "Олівець",
+ "tool.ellipse": "Еліпс",
+ "tool.embed": "Вбудовування",
+ "tool.eraser": "Гумка",
+ "tool.frame": "Рамка",
+ "tool.hand": "Рука",
+ "tool.hexagon": "Шестикутник",
+ "tool.highlight": "Текстовиділювач",
+ "tool.laser": "Лазер",
+ "tool.line": "Лінія",
+ "tool.note": "Нотатка",
+ "tool.octagon": "Восьмикутник",
+ "tool.oval": "Овал",
+ "tool.pentagon": "П'ятикутник",
+ "tool.rectangle": "Прямокутник",
+ "tool.rhombus": "Ромб",
+ "tool.select": "Переміщення",
+ "tool.star": "Зірка",
+ "tool.text": "Текст",
+ "tool.trapezoid": "Трапеція",
+ "tool.triangle": "Трикутник",
+ "tool.x-box": "X квадрат",
+ "vscode.file-open.backup": "Резервна копія",
+ "vscode.file-open.backup-failed": "Резервне копіювання не вдалося: це не файл .tldr.",
+ "vscode.file-open.backup-saved": "Резервна копія збережена",
+ "vscode.file-open.desc": "Цей файл було створено у попередній версії tldraw. Бажаєте оновити його для роботи з новою версією?",
+ "vscode.file-open.dont-show-again": "Більше не запитувати",
+ "vscode.file-open.open": "Далі"
+}
diff --git a/apps/web/public/translations/vi.json b/apps/web/public/translations/vi.json
new file mode 100644
index 00000000..4c3ac0d6
--- /dev/null
+++ b/apps/web/public/translations/vi.json
@@ -0,0 +1,295 @@
+{
+ "action.align-bottom": "Căn chỉnh dưới",
+ "action.align-center-horizontal": "Căn chỉnh ngang",
+ "action.align-center-horizontal.short": "Căn chỉnh ngang",
+ "action.align-center-vertical": "Căn chỉnh dọc",
+ "action.align-center-vertical.short": "Căn chỉnh dọc",
+ "action.align-left": "Căn chỉnh trái",
+ "action.align-right": "Căn chỉnh phải",
+ "action.align-top": "Căn chỉnh đầu",
+ "action.back-to-content": "Quay lại nội dung",
+ "action.bring-forward": "Mang ra phía sau",
+ "action.bring-to-front": "Mang ra phía trước",
+ "action.convert-to-bookmark": "Chuyển đổi thành Dấu trang",
+ "action.convert-to-embed": "Chuyển đổi sang Nhúng",
+ "action.copy": "Sao chép",
+ "action.copy-as-json": "Sao chép định dạng JSON",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "Sao chép định dạng PNG",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "Sao chép định dạng SVG",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "Cắt",
+ "action.delete": "Xoá",
+ "action.distribute-horizontal": "Phân phối ngang",
+ "action.distribute-horizontal.short": "Phân phối ngang",
+ "action.distribute-vertical": "Phân phối dọc",
+ "action.distribute-vertical.short": "Phân phối dọc",
+ "action.duplicate": "Nhân đôi",
+ "action.edit-link": "Chỉnh sửa liên kết",
+ "action.exit-pen-mode": "Thoát khỏi chế độ bút",
+ "action.export-as-json": "Xuất định dạng JSON",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "Xuất định dạng PNG",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "Xuất định dạng SVG",
+ "action.export-as-svg.short": "SVG",
+ "action.flip-horizontal": "Lật ngang",
+ "action.flip-horizontal.short": "Lật ngang",
+ "action.flip-vertical": "Lật dọc",
+ "action.flip-vertical.short": "Lật dọc",
+ "action.group": "Nhóm",
+ "action.insert-media": "Tải phương tiện lên",
+ "action.new-shared-project": "Dự án chia sẻ mới",
+ "action.open-embed-link": "Mở liên kết",
+ "action.open-file": "Mở tệp",
+ "action.pack": "Đóng gói",
+ "action.paste": "Dán",
+ "action.print": "In",
+ "action.redo": "Làm lại",
+ "action.rotate-ccw": "Xoay ngược chiều kim đồng hồ",
+ "action.rotate-cw": "Xoay theo chiều kim đồng hồ",
+ "action.save-copy": "Lưu bản sao",
+ "action.select-all": "Lựa chọn tất cả",
+ "action.select-none": "Không lựa chọn",
+ "action.send-backward": "Quay lại sau",
+ "action.send-to-back": "Quay lại trước",
+ "action.share-project": "Chia sẻ dự án này",
+ "action.stack-horizontal": "Căng chiều ngang",
+ "action.stack-horizontal.short": "Căng ngang",
+ "action.stack-vertical": "Căng chiều đứng",
+ "action.stack-vertical.short": "Căng dọc",
+ "action.stretch-horizontal": "Căng chiều ngang",
+ "action.stretch-horizontal.short": "Căng ngang",
+ "action.stretch-vertical": "Căng chiều đứng",
+ "action.stretch-vertical.short": "Căng dọc",
+ "action.toggle-auto-size": "Bật/tắt kích thước tự động",
+ "action.toggle-dark-mode": "Bật/tắt chế độ tối",
+ "action.toggle-dark-mode.menu": "Chế độ tối",
+ "action.toggle-debug-mode": "Bật/tắt chế độ tìm lỗi",
+ "action.toggle-debug-mode.menu": "Chế độ tìm lỗi",
+ "action.toggle-focus-mode": "Bật/tắt chế độ tập trung",
+ "action.toggle-focus-mode.menu": "Chế độ tập trung",
+ "action.toggle-grid": "Bật/tắt lưới",
+ "action.toggle-grid.menu": "Hiển thị lưới",
+ "action.toggle-snap-mode": "Bật/tắt luôn khớp",
+ "action.toggle-snap-mode.menu": "Luôn khớp",
+ "action.toggle-tool-lock": "Bật/tắt khóa công cụ",
+ "action.toggle-tool-lock.menu": "Khóa công cụ",
+ "action.toggle-transparent": "Bật/tắt nền trong suốt",
+ "action.toggle-transparent.context-menu": "Trong suốt",
+ "action.toggle-transparent.menu": "Trong suốt",
+ "action.undo": "Quay lại",
+ "action.ungroup": "Bỏ nhóm",
+ "action.zoom-in": "Phóng to",
+ "action.zoom-out": "Thu nhỏ",
+ "action.zoom-to-100": "Phóng to 100%",
+ "action.zoom-to-fit": "Phóng to vừa",
+ "action.zoom-to-selection": "Phóng to đến đang chọn",
+ "actions-menu.title": "Hành động",
+ "align-style.end": "Cuối",
+ "align-style.justify": "Căn chỉnh",
+ "align-style.middle": "Chính giữa",
+ "align-style.start": "Đầu",
+ "arrowheadEnd-style.arrow": "Mũi tên",
+ "arrowheadEnd-style.bar": "Thanh",
+ "arrowheadEnd-style.diamond": "Kim cương",
+ "arrowheadEnd-style.dot": "Chấm",
+ "arrowheadEnd-style.inverted": "Đảo ngược",
+ "arrowheadEnd-style.none": "Không",
+ "arrowheadEnd-style.pipe": "Đường ống",
+ "arrowheadEnd-style.square": "Hình vuông",
+ "arrowheadEnd-style.triangle": "Tam giác",
+ "arrowheadStart-style.arrow": "Mũi tên",
+ "arrowheadStart-style.bar": "Thanh",
+ "arrowheadStart-style.diamond": "Kim cương",
+ "arrowheadStart-style.dot": "Chấm",
+ "arrowheadStart-style.inverted": "Đảo ngược",
+ "arrowheadStart-style.none": "Không",
+ "arrowheadStart-style.pipe": "Đường ống",
+ "arrowheadStart-style.square": "Hình vuông",
+ "arrowheadStart-style.triangle": "Tam giác",
+ "color-style.black": "Đen",
+ "color-style.blue": "Xanh dương",
+ "color-style.green": "Xanh lá",
+ "color-style.grey": "Xám",
+ "color-style.light-blue": "Xanh dương tươi",
+ "color-style.light-green": "Xanh lá tươi",
+ "color-style.light-red": "Đỏ tươi",
+ "color-style.light-violet": "Tím tươi",
+ "color-style.orange": "Cam",
+ "color-style.red": "Đỏ",
+ "color-style.violet": "Tím",
+ "color-style.yellow": "Vàng",
+ "context-menu.arrange": "Vị trí",
+ "context-menu.copy-as": "Sao chép dưới dạng",
+ "context-menu.export-as": "Xuất dưới dạng",
+ "context-menu.move-to-page": "Đi đến trang",
+ "context-menu.reorder": "Sắp xếp",
+ "context.pages.new-page": "Trang mới",
+ "dash-style.dashed": "Nét đứt",
+ "dash-style.dotted": "Chấm",
+ "dash-style.draw": "Vẽ",
+ "dash-style.solid": "Trơn",
+ "edit-link-dialog.cancel": "Huỷ",
+ "edit-link-dialog.clear": "Xoá",
+ "edit-link-dialog.detail": "Liên kết sẽ mở trong một tab mới.",
+ "edit-link-dialog.invalid-url": "Một liên kết phải là một đường dẫn hợp lệ.",
+ "edit-link-dialog.save": "Tiếp tục",
+ "edit-link-dialog.title": "Chỉnh sửa liên kết",
+ "edit-link-dialog.url": "Đường dẫn",
+ "edit-pages-dialog.move-down": "Xuống dưới",
+ "edit-pages-dialog.move-up": "Lên trên",
+ "embed-dialog.back": "Quay lại",
+ "embed-dialog.cancel": "Huỷ",
+ "embed-dialog.create": "Tạo",
+ "embed-dialog.instruction": "Dán đường dẫn của trang web để tạo nhúng.",
+ "embed-dialog.invalid-url": "Chúng tôi không thể tạo nhúng từ đường dẫn đó.",
+ "embed-dialog.title": "Tạo nhúng",
+ "embed-dialog.url": "Đường dẫn",
+ "file-system.confirm-open.cancel": "Huỷ",
+ "file-system.confirm-open.description": "Việc mở tệp sẽ thay thế dự án hiện tại của bạn và mọi thay đổi chưa được lưu sẽ bị mất. Bạn có chắc chắn muốn tiếp tục không?",
+ "file-system.confirm-open.dont-show-again": "Đừng hỏi lại",
+ "file-system.confirm-open.open": "Mở tệp",
+ "file-system.confirm-open.title": "Ghi đè lên dự án hiện tại?",
+ "file-system.file-open-error.file-format-version-too-new": "Tệp bạn cố mở là từ phiên bản mới hơn của tldraw. Hãy tải lại trang và thử lại.",
+ "file-system.file-open-error.generic-corrupted-file": "Tệp bạn cố mở bị hỏng.",
+ "file-system.file-open-error.not-a-tldraw-file": "Tệp bạn cố mở không giống tệp tldraw.",
+ "file-system.file-open-error.title": "Không thể mở tệp",
+ "file-system.shared-document-file-open-error.description": "Mở tệp từ các dự án được chia sẻ hiện tại không được hỗ trợ.",
+ "file-system.shared-document-file-open-error.title": "Không thể mở tệp",
+ "fill-style.none": "Không",
+ "fill-style.pattern": "Mẫu",
+ "fill-style.semi": "Một nữa",
+ "fill-style.solid": "Trơn",
+ "focus-mode.toggle-focus-mode": "Bật/tắt chế độ tập trung",
+ "font-style.draw": "Vẽ",
+ "font-style.mono": "Mono",
+ "font-style.sans": "Sans",
+ "font-style.serif": "Serif",
+ "geo-style.arrow-down": "Mũi tên xuống dưới",
+ "geo-style.arrow-left": "Mũi tên sang trái",
+ "geo-style.arrow-right": "Mũi tên sang phải",
+ "geo-style.arrow-up": "Mũi tên hướng lên",
+ "geo-style.diamond": "Kim cương",
+ "geo-style.ellipse": "Hình Elip",
+ "geo-style.hexagon": "Hình lục giác",
+ "geo-style.octagon": "Hình bát giác",
+ "geo-style.oval": "Hình bầu",
+ "geo-style.pentagon": "Hình ngũ giác",
+ "geo-style.rectangle": "Hình chữ nhật",
+ "geo-style.rhombus": "Hình thoi",
+ "geo-style.rhombus-2": "Hình thoi 2",
+ "geo-style.star": "Ngôi sao",
+ "geo-style.trapezoid": "Hình thang",
+ "geo-style.triangle": "Hình tam giác",
+ "geo-style.x-box": "Khung X",
+ "help-menu.about": "Về chúng tôi",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "Github",
+ "help-menu.keyboard-shortcuts": "Phím tắt bàn phím",
+ "help-menu.title": "Trợ giúp và thông tin khác",
+ "help-menu.twitter": "Twitter",
+ "menu.copy-as": "Sao chép dưới dạng",
+ "menu.edit": "Chỉnh sửa",
+ "menu.export-as": "Xuất dưới dạng",
+ "menu.file": "Tệp",
+ "menu.language": "Ngôn ngữ",
+ "menu.preferences": "Cài đặt",
+ "menu.title": "Menu",
+ "menu.view": "Lượt xem",
+ "navigation-zone.toggle-minimap": "Bật/tắt bản đồ nhỏ",
+ "navigation-zone.zoom": "Phóng to",
+ "opacity-style.0.1": "10%",
+ "opacity-style.0.25": "25%",
+ "opacity-style.0.5": "50%",
+ "opacity-style.0.75": "75%",
+ "opacity-style.1": "100%",
+ "page-menu.create-new-page": "Tạo trang mới",
+ "page-menu.edit-done": "Xong",
+ "page-menu.edit-start": "Sửa",
+ "page-menu.max-page-count-reached": "Đã đạt đến số trang tối đa",
+ "page-menu.new-page-initial-name": "Trang 1",
+ "page-menu.submenu.delete": "Xoá",
+ "page-menu.submenu.duplicate-page": "Nhân đôi",
+ "page-menu.submenu.move-down": "Đi xuống",
+ "page-menu.submenu.move-up": "Đi lên",
+ "page-menu.submenu.rename": "Đổi tên",
+ "page-menu.submenu.title": "Menu",
+ "page-menu.title": "Các trang",
+ "people-menu.change-color": "Thay đổi màu",
+ "people-menu.change-name": "Thay đổi tên",
+ "people-menu.invite": "Mời người khác",
+ "people-menu.title": "Mọi người",
+ "people-menu.user": "(Bạn)",
+ "share-menu.copy-link": "Sao chép liên kết",
+ "share-menu.copy-link-note": "Bất kỳ ai có liên kết đều có thể xem và chỉnh sửa dự án này.",
+ "share-menu.copy-readonly-link": "Sao chép liên kết chỉ có thể đọc",
+ "share-menu.copy-readonly-link-note": "Bất kỳ ai có liên kết đều có thể xem (nhưng không thể chỉnh sửa) dự án này.",
+ "share-menu.offline-note": "Chia sẻ dự án này sẽ tạo một bản sao trực tiếp được lưu trữ tại một đường dẫn mới. Bạn có thể chia sẻ đường dẫn với tối đa 30 người khác để cùng xem và chỉnh sửa dự án.",
+ "share-menu.project-too-large": "Rất tiếc, không thể chia sẻ dự án này vì dự án quá lớn. Chúng tôi đang làm việc để cải thiện điều này!",
+ "share-menu.readonly-link": "Chỉ có thể đọc",
+ "share-menu.share-project": "Chia sẻ dự án này",
+ "share-menu.title": "Chia sẻ",
+ "shortcuts-dialog.edit": "Sửa",
+ "shortcuts-dialog.file": "Tệp",
+ "shortcuts-dialog.preferences": "Cài đặt",
+ "shortcuts-dialog.title": "Phím tắt bàn phím",
+ "shortcuts-dialog.tools": "Các công cụ",
+ "shortcuts-dialog.transform": "Biến đổi",
+ "shortcuts-dialog.view": "Xem",
+ "size-style.l": "Lớn",
+ "size-style.m": "Trung bình",
+ "size-style.s": "Nhỏ",
+ "size-style.xl": "Cực lớn",
+ "spline-style.cubic": "Khối",
+ "spline-style.line": "Đường",
+ "style-panel.align": "Căn chỉnh",
+ "style-panel.arrowheads": "Đầu mũi tên",
+ "style-panel.color": "Màu",
+ "style-panel.dash": "Nét đứt",
+ "style-panel.fill": "Tô",
+ "style-panel.font": "Phông",
+ "style-panel.geo": "Hình",
+ "style-panel.mixed": "Trộn",
+ "style-panel.opacity": "Độ mờ",
+ "style-panel.size": "Kích thước",
+ "style-panel.spline": "Đường cong",
+ "style-panel.title": "Kiểu",
+ "toast.close": "Đóng",
+ "toast.error.copy-fail.desc": "Không thể sao chép hình ảnh",
+ "toast.error.copy-fail.title": "Sao chép thất bại",
+ "toast.error.export-fail.desc": "Xuất ảnh thất bại",
+ "toast.error.export-fail.title": "Xuất thất bại",
+ "tool-panel.drawing": "Vẽ",
+ "tool-panel.shapes": "Các khối",
+ "tool.arrow": "Mũi tên",
+ "tool.arrow-down": "Mũi tên xuống dưới",
+ "tool.arrow-left": "Mũi tên qua trái",
+ "tool.arrow-right": "Mũi tên qua phải",
+ "tool.arrow-up": "Mũi tên lên trên",
+ "tool.asset": "Thư viện",
+ "tool.diamond": "Hình kim cương",
+ "tool.draw": "Vẽ",
+ "tool.ellipse": "Hình Elip",
+ "tool.embed": "Nhúng",
+ "tool.eraser": "Tẩy",
+ "tool.frame": "Khung",
+ "tool.hand": "Tay",
+ "tool.hexagon": "Hình lục giác",
+ "tool.line": "Đường",
+ "tool.note": "Ghi chú",
+ "tool.octagon": "Hình bát giác",
+ "tool.oval": "Hình bầu",
+ "tool.pentagon": "Hình ngũ giác",
+ "tool.rectangle": "Hình chữ nhật",
+ "tool.rhombus": "Hình thoi",
+ "tool.select": "Lựa chọn",
+ "tool.star": "Ngôi sao",
+ "tool.text": "Chữ",
+ "tool.trapezoid": "Hình thang",
+ "tool.triangle": "Tam giác",
+ "tool.x-box": "Khung X",
+ "vscode.file-open.desc": "Tệp này được tạo bằng phiên bản cũ hơn của tldraw. Bạn có muốn cập nhật nó để hoạt động với phiên bản mới không?",
+ "vscode.file-open.dont-show-again": "Đừng hỏi lại"
+}
diff --git a/apps/web/public/translations/zh-cn.json b/apps/web/public/translations/zh-cn.json
new file mode 100644
index 00000000..e0f98ee1
--- /dev/null
+++ b/apps/web/public/translations/zh-cn.json
@@ -0,0 +1,353 @@
+{
+ "action.align-bottom": "底端对齐",
+ "action.align-center-horizontal": "水平对齐",
+ "action.align-center-horizontal.short": "水平对齐",
+ "action.align-center-vertical": "垂直对齐",
+ "action.align-center-vertical.short": "垂直对齐",
+ "action.align-left": "左对齐",
+ "action.align-right": "右对齐",
+ "action.align-top": "顶端对齐",
+ "action.back-to-content": "返回内容",
+ "action.bring-forward": "上移一层",
+ "action.bring-to-front": "置顶",
+ "action.convert-to-bookmark": "转换为书签",
+ "action.convert-to-embed": "转换为嵌入",
+ "action.copy": "复制",
+ "action.copy-as-json": "复制为 JSON",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "复制为 PNG",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "复制为 SVG",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "剪切",
+ "action.delete": "删除",
+ "action.distribute-horizontal": "横向分布",
+ "action.distribute-horizontal.short": "横向分布",
+ "action.distribute-vertical": "纵向分布",
+ "action.distribute-vertical.short": "纵向分布",
+ "action.duplicate": "复制",
+ "action.edit-link": "编辑链接",
+ "action.exit-pen-mode": "退出钢笔模式",
+ "action.export-as-json": "导出为 JSON",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "导出为 PNG",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "导出为 SVG",
+ "action.export-as-svg.short": "SVG",
+ "action.fit-frame-to-content": "适合于内容",
+ "action.flip-horizontal": "水平翻转",
+ "action.flip-horizontal.short": "水平翻转",
+ "action.flip-vertical": "垂直翻转",
+ "action.flip-vertical.short": "垂直翻转",
+ "action.fork-project": "Fork 这个项目",
+ "action.group": "分组",
+ "action.insert-embed": "创建嵌入",
+ "action.insert-media": "上传媒体文件",
+ "action.leave-shared-project": "退出共享项目",
+ "action.new-project": "新项目",
+ "action.new-shared-project": "新建共享项目",
+ "action.open-cursor-chat": "Cursor Chat",
+ "action.open-embed-link": "打开链接",
+ "action.open-file": "打开文件",
+ "action.pack": "打包",
+ "action.paste": "粘贴",
+ "action.print": "打印",
+ "action.redo": "重做",
+ "action.remove-frame": "移除框架",
+ "action.rotate-ccw": "逆时针旋转",
+ "action.rotate-cw": "顺时针旋转",
+ "action.save-copy": "保存副本",
+ "action.select-all": "选中全部",
+ "action.select-none": "取消选中",
+ "action.send-backward": "下移一层",
+ "action.send-to-back": "置底",
+ "action.share-project": "共享此项目",
+ "action.stack-horizontal": "横排",
+ "action.stack-horizontal.short": "横排",
+ "action.stack-vertical": "竖排",
+ "action.stack-vertical.short": "竖排",
+ "action.stop-following": "停止跟踪",
+ "action.stretch-horizontal": "水平拉伸",
+ "action.stretch-horizontal.short": "水平拉伸",
+ "action.stretch-vertical": "垂直拉伸",
+ "action.stretch-vertical.short": "垂直拉伸",
+ "action.toggle-auto-size": "切换自动大小",
+ "action.toggle-dark-mode": "切换暗黑模式",
+ "action.toggle-dark-mode.menu": "暗黑模式",
+ "action.toggle-debug-mode": "切换调试模式",
+ "action.toggle-debug-mode.menu": "调试模式",
+ "action.toggle-focus-mode": "切换专注模式",
+ "action.toggle-focus-mode.menu": "专注模式",
+ "action.toggle-grid": "切换网格",
+ "action.toggle-grid.menu": "显示网格",
+ "action.toggle-lock": "锁定/解锁",
+ "action.toggle-reduce-motion": "切换降低灵敏度",
+ "action.toggle-reduce-motion.menu": "降低灵敏度",
+ "action.toggle-snap-mode": "切换始终吸附",
+ "action.toggle-snap-mode.menu": "始终吸附",
+ "action.toggle-tool-lock": "切换工具锁定",
+ "action.toggle-tool-lock.menu": "工具锁定",
+ "action.toggle-transparent": "切换透明背景",
+ "action.toggle-transparent.context-menu": "透明",
+ "action.toggle-transparent.menu": "透明",
+ "action.undo": "撤销",
+ "action.ungroup": "取消分组",
+ "action.unlock-all": "全部解锁",
+ "action.zoom-in": "放大",
+ "action.zoom-out": "缩小",
+ "action.zoom-to-100": "缩放至 100%",
+ "action.zoom-to-fit": "自适应缩放",
+ "action.zoom-to-selection": "缩放至显示选中内容",
+ "actions-menu.title": "操作",
+ "align-style.end": "结束",
+ "align-style.justify": "两端对齐",
+ "align-style.middle": "中间",
+ "align-style.start": "开始",
+ "arrowheadEnd-style.arrow": "箭头",
+ "arrowheadEnd-style.bar": "条",
+ "arrowheadEnd-style.diamond": "菱形",
+ "arrowheadEnd-style.dot": "点",
+ "arrowheadEnd-style.inverted": "反转",
+ "arrowheadEnd-style.none": "无",
+ "arrowheadEnd-style.pipe": "管道",
+ "arrowheadEnd-style.square": "正方形",
+ "arrowheadEnd-style.triangle": "三角形",
+ "arrowheadStart-style.arrow": "箭头",
+ "arrowheadStart-style.bar": "条",
+ "arrowheadStart-style.diamond": "菱形",
+ "arrowheadStart-style.dot": "点",
+ "arrowheadStart-style.inverted": "反转",
+ "arrowheadStart-style.none": "无",
+ "arrowheadStart-style.pipe": "管道",
+ "arrowheadStart-style.square": "正方形",
+ "arrowheadStart-style.triangle": "三角形",
+ "color-style.black": "黑色",
+ "color-style.blue": "蓝色",
+ "color-style.green": "绿色",
+ "color-style.grey": "灰色",
+ "color-style.light-blue": "浅蓝色",
+ "color-style.light-green": "浅绿色",
+ "color-style.light-red": "浅红色",
+ "color-style.light-violet": "浅紫色",
+ "color-style.orange": "橙色",
+ "color-style.red": "红色",
+ "color-style.violet": "紫色",
+ "color-style.yellow": "黄色",
+ "context-menu.arrange": "排列",
+ "context-menu.copy-as": "复制为",
+ "context-menu.export-as": "导出为",
+ "context-menu.move-to-page": "移动到页面",
+ "context-menu.reorder": "重新排序",
+ "context.pages.new-page": "新页面",
+ "cursor-chat.type-to-chat": "输入以开始聊天...",
+ "dash-style.dashed": "虚线",
+ "dash-style.dotted": "虚点",
+ "dash-style.draw": "画笔",
+ "dash-style.solid": "实心",
+ "debug-panel.more": "更多",
+ "edit-link-dialog.cancel": "取消",
+ "edit-link-dialog.clear": "清除",
+ "edit-link-dialog.detail": "链接将在新标签页中打开。",
+ "edit-link-dialog.invalid-url": "链接必须是有效 URL。",
+ "edit-link-dialog.save": "继续",
+ "edit-link-dialog.title": "编辑链接",
+ "edit-link-dialog.url": "URL",
+ "edit-pages-dialog.move-down": "下移",
+ "edit-pages-dialog.move-up": "上移",
+ "embed-dialog.back": "返回",
+ "embed-dialog.cancel": "取消",
+ "embed-dialog.create": "创建",
+ "embed-dialog.instruction": "粘贴网站 URL 创建嵌入。",
+ "embed-dialog.invalid-url": "我们无法从该 URL 创建嵌入。",
+ "embed-dialog.title": "创建嵌入",
+ "embed-dialog.url": "URL",
+ "file-system.confirm-clear.cancel": "取消",
+ "file-system.confirm-clear.continue": "继续",
+ "file-system.confirm-clear.description": "创建新项目将清除您当前的项目,并且所有未保存的更改都将丢失。确定要继续吗?",
+ "file-system.confirm-clear.dont-show-again": "不再询问",
+ "file-system.confirm-clear.title": "清空当前项目?",
+ "file-system.confirm-open.cancel": "取消",
+ "file-system.confirm-open.description": "打开文件将替换您的当前项目,任何未保存的更改将丢失。确定要继续吗?",
+ "file-system.confirm-open.dont-show-again": "不再询问",
+ "file-system.confirm-open.open": "打开文件",
+ "file-system.confirm-open.title": "覆盖当前项目?",
+ "file-system.file-open-error.file-format-version-too-new": "您试图打开的文件来自 tldraw 新版本。请重新加载页面,然后重试。",
+ "file-system.file-open-error.generic-corrupted-file": "您试图打开的文件已损坏。",
+ "file-system.file-open-error.not-a-tldraw-file": "您试图打开的文件看起来不像 tldraw 文件。",
+ "file-system.file-open-error.title": "无法打开文件",
+ "file-system.shared-document-file-open-error.description": "不支持从共享项目中打开文件。",
+ "file-system.shared-document-file-open-error.title": "无法打开文件",
+ "fill-style.none": "无",
+ "fill-style.pattern": "图案",
+ "fill-style.semi": "半填充",
+ "fill-style.solid": "实心",
+ "focus-mode.toggle-focus-mode": "切换专注模式",
+ "font-style.draw": "画笔",
+ "font-style.mono": "黑白",
+ "font-style.sans": "无衬线",
+ "font-style.serif": "衬线",
+ "geo-style.arrow-down": "向下箭头",
+ "geo-style.arrow-left": "向左箭头",
+ "geo-style.arrow-right": "向右箭头",
+ "geo-style.arrow-up": "向上箭头",
+ "geo-style.check-box": "复选框",
+ "geo-style.cloud": "云",
+ "geo-style.diamond": "菱形",
+ "geo-style.ellipse": "椭圆形",
+ "geo-style.hexagon": "六边形",
+ "geo-style.octagon": "八边形",
+ "geo-style.oval": "卵形",
+ "geo-style.pentagon": "五边形",
+ "geo-style.rectangle": "矩形",
+ "geo-style.rhombus": "菱形",
+ "geo-style.rhombus-2": "菱形 2",
+ "geo-style.star": "星形",
+ "geo-style.trapezoid": "梯形",
+ "geo-style.triangle": "三角形",
+ "geo-style.x-box": "X 框",
+ "help-menu.about": "关于",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "键盘快捷方式",
+ "help-menu.title": "帮助和资源",
+ "help-menu.twitter": "Twitter",
+ "home-project-dialog.description": "这是您的主项目,它只为您而设!",
+ "home-project-dialog.ok": "好的",
+ "home-project-dialog.title": "主项目",
+ "menu.copy-as": "复制为",
+ "menu.edit": "编辑",
+ "menu.export-as": "导出为",
+ "menu.file": "文件",
+ "menu.language": "语言",
+ "menu.preferences": "偏好",
+ "menu.title": "菜单",
+ "menu.view": "视图",
+ "navigation-zone.toggle-minimap": "切换小地图",
+ "navigation-zone.zoom": "缩放",
+ "opacity-style.0.1": "10%",
+ "opacity-style.0.25": "25%",
+ "opacity-style.0.5": "50%",
+ "opacity-style.0.75": "75%",
+ "opacity-style.1": "100%",
+ "page-menu.create-new-page": "创建新页面",
+ "page-menu.edit-done": "完成",
+ "page-menu.edit-start": "编辑",
+ "page-menu.go-to-page": "转到页面",
+ "page-menu.max-page-count-reached": "达到最大页数",
+ "page-menu.new-page-initial-name": "页面 1",
+ "page-menu.submenu.delete": "删除",
+ "page-menu.submenu.duplicate-page": "复制",
+ "page-menu.submenu.move-down": "下移",
+ "page-menu.submenu.move-up": "上移",
+ "page-menu.submenu.rename": "重命名",
+ "page-menu.submenu.title": "菜单",
+ "page-menu.title": "页面",
+ "people-menu.change-color": "更改颜色",
+ "people-menu.change-name": "更改名称",
+ "people-menu.follow": "跟踪",
+ "people-menu.following": "正在跟随",
+ "people-menu.invite": "邀请他人",
+ "people-menu.leading": "跟随",
+ "people-menu.title": "人员",
+ "people-menu.user": "(您)",
+ "rename-project-dialog.cancel": "取消",
+ "rename-project-dialog.rename": "重命名",
+ "rename-project-dialog.title": "项目重命名",
+ "share-menu.copy-link": "复制链接",
+ "share-menu.copy-link-note": "任何人使用此链接都能查看和编辑此项目。",
+ "share-menu.copy-readonly-link": "复制只读链接",
+ "share-menu.copy-readonly-link-note": "任何人使用此链接都能查看(但不能编辑)此项目。",
+ "share-menu.create-snapshot-link": "创建快照链接",
+ "share-menu.default-project-name": "共享项目",
+ "share-menu.fork-note": "基于这个快照创建一个新的共享项目。",
+ "share-menu.offline-note": "共享此项目将以新 URL 创建托管活动副本。您可以与不超过三十人共享此 URL,一起查看和编辑项目。",
+ "share-menu.project-too-large": "抱歉,此项目太大,无法共享。我们正在努力解决!",
+ "share-menu.readonly-link": "只读",
+ "share-menu.save-note": "将这个项目以 .tldr 格式保存到您的电脑上。",
+ "share-menu.share-project": "共享此项目",
+ "share-menu.snapshot-link-note": "截取此项目并将其共享为一个只读的快照链接。",
+ "share-menu.title": "共享",
+ "share-menu.upload-failed": "抱歉,我们目前无法上传您的项目,请重试。如果问题仍然存在,请告诉我们。",
+ "sharing.confirm-leave.cancel": "取消",
+ "sharing.confirm-leave.description": "您确定要退出这个共享项目吗?您可以通过访问它的 URL 以返回。",
+ "sharing.confirm-leave.dont-show-again": "不再询问",
+ "sharing.confirm-leave.leave": "退出",
+ "sharing.confirm-leave.title": "退出当前项目?",
+ "shortcuts-dialog.collaboration": "协作",
+ "shortcuts-dialog.edit": "编辑",
+ "shortcuts-dialog.file": "文件",
+ "shortcuts-dialog.preferences": "偏好",
+ "shortcuts-dialog.title": "键盘快捷方式",
+ "shortcuts-dialog.tools": "工具",
+ "shortcuts-dialog.transform": "转换",
+ "shortcuts-dialog.view": "视图",
+ "size-style.l": "大",
+ "size-style.m": "中",
+ "size-style.s": "小",
+ "size-style.xl": "加大",
+ "spline-style.cubic": "立方形",
+ "spline-style.line": "直线",
+ "status.offline": "离线",
+ "status.online": "在线",
+ "style-panel.align": "对齐",
+ "style-panel.arrowhead-end": "末端",
+ "style-panel.arrowhead-start": "前端",
+ "style-panel.arrowheads": "箭头",
+ "style-panel.color": "颜色",
+ "style-panel.dash": "划线",
+ "style-panel.fill": "填充",
+ "style-panel.font": "字体",
+ "style-panel.geo": "形状",
+ "style-panel.mixed": "混合",
+ "style-panel.opacity": "不透明度",
+ "style-panel.position": "位置",
+ "style-panel.size": "大小",
+ "style-panel.spline": "曲线",
+ "style-panel.title": "样式",
+ "style-panel.vertical-align": "垂直对齐",
+ "toast.close": "关闭",
+ "toast.error.copy-fail.desc": "无法复制图像",
+ "toast.error.copy-fail.title": "复制失败",
+ "toast.error.export-fail.desc": "无法导出图像",
+ "toast.error.export-fail.title": "导出失败",
+ "tool-panel.drawing": "绘图",
+ "tool-panel.more": "更多",
+ "tool-panel.shapes": "形状",
+ "tool.arrow": "箭头",
+ "tool.arrow-down": "向下箭头",
+ "tool.arrow-left": "向左箭头",
+ "tool.arrow-right": "向右箭头",
+ "tool.arrow-up": "向上箭头",
+ "tool.asset": "图片",
+ "tool.check-box": "复选框",
+ "tool.cloud": "云",
+ "tool.diamond": "菱形",
+ "tool.draw": "画笔",
+ "tool.ellipse": "椭圆形",
+ "tool.embed": "嵌入",
+ "tool.eraser": "橡皮",
+ "tool.frame": "框架",
+ "tool.hand": "手形",
+ "tool.hexagon": "六边形",
+ "tool.highlight": "高亮",
+ "tool.laser": "激光笔",
+ "tool.line": "直线",
+ "tool.note": "便笺",
+ "tool.octagon": "八边形",
+ "tool.oval": "卵形",
+ "tool.pentagon": "五边形",
+ "tool.rectangle": "矩形",
+ "tool.rhombus": "菱形",
+ "tool.select": "选择",
+ "tool.star": "星形",
+ "tool.text": "文本",
+ "tool.trapezoid": "梯形",
+ "tool.triangle": "三角形",
+ "tool.x-box": "X 框",
+ "vscode.file-open.backup": "备份",
+ "vscode.file-open.backup-failed": "备份失败:这不是 .tldr 文件。",
+ "vscode.file-open.backup-saved": "备份已保存",
+ "vscode.file-open.desc": "该文件是用较早版本的tldraw创建的。您是否愿意将其更新为新版本?",
+ "vscode.file-open.dont-show-again": "不再询问",
+ "vscode.file-open.open": "继续"
+}
diff --git a/apps/web/public/translations/zh-tw.json b/apps/web/public/translations/zh-tw.json
new file mode 100644
index 00000000..2ae75700
--- /dev/null
+++ b/apps/web/public/translations/zh-tw.json
@@ -0,0 +1,316 @@
+{
+ "action.align-bottom": "置底",
+ "action.align-center-horizontal": "水平置中",
+ "action.align-center-horizontal.short": "水平置中",
+ "action.align-center-vertical": "垂直置中",
+ "action.align-center-vertical.short": "垂直置中",
+ "action.align-left": "置左",
+ "action.align-right": "置右",
+ "action.align-top": "置頂",
+ "action.back-to-content": "回到內容",
+ "action.bring-forward": "前移一層",
+ "action.bring-to-front": "移至頂層",
+ "action.convert-to-bookmark": "轉換為書籤",
+ "action.convert-to-embed": "轉換為嵌入",
+ "action.copy": "複製",
+ "action.copy-as-json": "複製為 JSON",
+ "action.copy-as-json.short": "JSON",
+ "action.copy-as-png": "複製為 PNG",
+ "action.copy-as-png.short": "PNG",
+ "action.copy-as-svg": "複製為 SVG",
+ "action.copy-as-svg.short": "SVG",
+ "action.cut": "剪下",
+ "action.delete": "刪除",
+ "action.distribute-horizontal": "水平分布",
+ "action.distribute-horizontal.short": "水平分布",
+ "action.distribute-vertical": "垂直分布",
+ "action.distribute-vertical.short": "垂直分布",
+ "action.duplicate": "複製",
+ "action.edit-link": "編輯連結",
+ "action.exit-pen-mode": "退出畫筆模式",
+ "action.export-as-json": "匯出成 JSON",
+ "action.export-as-json.short": "JSON",
+ "action.export-as-png": "匯出成 PNG",
+ "action.export-as-png.short": "PNG",
+ "action.export-as-svg": "匯出成 SVG",
+ "action.export-as-svg.short": "SVG",
+ "action.flip-horizontal": "水平翻轉",
+ "action.flip-horizontal.short": "水平翻轉",
+ "action.flip-vertical": "垂直翻轉",
+ "action.flip-vertical.short": "垂直翻轉",
+ "action.group": "群組",
+ "action.insert-media": "上傳媒體檔",
+ "action.new-project": "新建專案",
+ "action.new-shared-project": "新建共享專案",
+ "action.open-embed-link": "開啟連結",
+ "action.open-file": "開啟檔案",
+ "action.pack": "打包",
+ "action.paste": "貼上",
+ "action.print": "列印",
+ "action.redo": "取消復原",
+ "action.rotate-ccw": "逆時針旋轉",
+ "action.rotate-cw": "順時針旋轉",
+ "action.save-copy": "存為副本",
+ "action.select-all": "全選",
+ "action.select-none": "取消選取",
+ "action.send-backward": "後移一層",
+ "action.send-to-back": "移至底層",
+ "action.share-project": "共享此專案",
+ "action.stack-horizontal": "水平堆疊",
+ "action.stack-horizontal.short": "水平堆疊",
+ "action.stack-vertical": "垂直堆疊",
+ "action.stack-vertical.short": "垂直堆疊",
+ "action.stop-following": "取消追蹤",
+ "action.stretch-horizontal": "水平延展",
+ "action.stretch-horizontal.short": "水平延展",
+ "action.stretch-vertical": "垂直延展",
+ "action.stretch-vertical.short": "垂直延展",
+ "action.toggle-auto-size": "切換自動大小",
+ "action.toggle-dark-mode": "切換深色模式",
+ "action.toggle-dark-mode.menu": "深色模式",
+ "action.toggle-debug-mode": "切換除錯模式",
+ "action.toggle-debug-mode.menu": "除錯模式",
+ "action.toggle-focus-mode": "切換專注模式",
+ "action.toggle-focus-mode.menu": "專注模式",
+ "action.toggle-grid": "切換網格",
+ "action.toggle-grid.menu": "顯示網格",
+ "action.toggle-snap-mode": "切換始終貼齊",
+ "action.toggle-snap-mode.menu": "始終貼齊",
+ "action.toggle-tool-lock": "切換工具鎖定",
+ "action.toggle-tool-lock.menu": "工具鎖定",
+ "action.toggle-transparent": "切換透明背景",
+ "action.toggle-transparent.context-menu": "透明",
+ "action.toggle-transparent.menu": "透明",
+ "action.undo": "復原",
+ "action.ungroup": "取消群組",
+ "action.zoom-in": "放大",
+ "action.zoom-out": "縮小",
+ "action.zoom-to-100": "縮放至 100%",
+ "action.zoom-to-fit": "縮放至適當大小",
+ "action.zoom-to-selection": "縮放至選取範圍",
+ "actions-menu.title": "操作",
+ "align-style.end": "末端",
+ "align-style.justify": "兩端",
+ "align-style.middle": "置中",
+ "align-style.start": "前端",
+ "arrowheadEnd-style.arrow": "箭頭",
+ "arrowheadEnd-style.bar": "條狀",
+ "arrowheadEnd-style.diamond": "菱形",
+ "arrowheadEnd-style.dot": "點",
+ "arrowheadEnd-style.inverted": "反轉",
+ "arrowheadEnd-style.none": "無",
+ "arrowheadEnd-style.pipe": "管狀",
+ "arrowheadEnd-style.square": "正方形",
+ "arrowheadEnd-style.triangle": "三角形",
+ "arrowheadStart-style.arrow": "箭頭",
+ "arrowheadStart-style.bar": "條狀",
+ "arrowheadStart-style.diamond": "菱形",
+ "arrowheadStart-style.dot": "點",
+ "arrowheadStart-style.inverted": "反轉",
+ "arrowheadStart-style.none": "無",
+ "arrowheadStart-style.pipe": "管狀",
+ "arrowheadStart-style.square": "正方形",
+ "arrowheadStart-style.triangle": "三角形",
+ "color-style.black": "黑色",
+ "color-style.blue": "藍色",
+ "color-style.green": "綠色",
+ "color-style.grey": "灰色",
+ "color-style.light-blue": "淺藍色",
+ "color-style.light-green": "淺綠色",
+ "color-style.light-red": "淺紅色",
+ "color-style.light-violet": "淺紫色",
+ "color-style.orange": "橘色",
+ "color-style.red": "紅色",
+ "color-style.violet": "紫色",
+ "color-style.yellow": "黃色",
+ "context-menu.arrange": "佈局",
+ "context-menu.copy-as": "複製成",
+ "context-menu.export-as": "匯出成",
+ "context-menu.move-to-page": "移至頁面",
+ "context-menu.reorder": "重新排序",
+ "context.pages.new-page": "新頁面",
+ "dash-style.dashed": "虛線",
+ "dash-style.dotted": "虛點",
+ "dash-style.draw": "手繪",
+ "dash-style.solid": "實線",
+ "debug-panel.more": "更多",
+ "edit-link-dialog.cancel": "取消",
+ "edit-link-dialog.clear": "清除",
+ "edit-link-dialog.detail": "連結將在新分頁開啟。",
+ "edit-link-dialog.invalid-url": "連結必須是有效網址。",
+ "edit-link-dialog.save": "繼續",
+ "edit-link-dialog.title": "編輯連結",
+ "edit-link-dialog.url": "網址",
+ "edit-pages-dialog.move-down": "下移",
+ "edit-pages-dialog.move-up": "上移",
+ "embed-dialog.back": "返回",
+ "embed-dialog.cancel": "取消",
+ "embed-dialog.create": "建立",
+ "embed-dialog.instruction": "貼上網站連結以建立嵌入。",
+ "embed-dialog.invalid-url": "我們沒辦法從該網址建立嵌入。",
+ "embed-dialog.title": "建立嵌入",
+ "embed-dialog.url": "網址",
+ "file-system.confirm-clear.cancel": "取消",
+ "file-system.confirm-clear.continue": "繼續",
+ "file-system.confirm-clear.description": "建立新專案將清除當前專案,任何未儲存的更動都會消失。您確定要繼續嗎?",
+ "file-system.confirm-clear.dont-show-again": "不再詢問",
+ "file-system.confirm-clear.title": "清除當前專案?",
+ "file-system.confirm-open.cancel": "取消",
+ "file-system.confirm-open.description": "開啟檔案將置換您的當前專案,任何未儲存的更動都會消失。您確定要繼續嗎?",
+ "file-system.confirm-open.dont-show-again": "不再詢問",
+ "file-system.confirm-open.open": "開啟檔案",
+ "file-system.confirm-open.title": "覆蓋當前檔案?",
+ "file-system.file-open-error.file-format-version-too-new": "你嘗試開啟的檔案來自新版 tldraw。請重新載入頁面,然後再試一次。",
+ "file-system.file-open-error.generic-corrupted-file": "您嘗試開啟的檔案已損毀。",
+ "file-system.file-open-error.not-a-tldraw-file": "您嘗試開啟的檔案不像 tldraw 檔。",
+ "file-system.file-open-error.title": "無法開啟檔案",
+ "file-system.shared-document-file-open-error.description": "不支援從共享專案開啟檔案。",
+ "file-system.shared-document-file-open-error.title": "無法開啟檔案",
+ "fill-style.none": "無",
+ "fill-style.pattern": "圖案",
+ "fill-style.semi": "半填滿",
+ "fill-style.solid": "填滿",
+ "focus-mode.toggle-focus-mode": "切換專注模式",
+ "font-style.draw": "手繪",
+ "font-style.mono": "等寬",
+ "font-style.sans": "無襯線",
+ "font-style.serif": "襯線",
+ "geo-style.arrow-down": "向下箭頭",
+ "geo-style.arrow-left": "向左箭頭",
+ "geo-style.arrow-right": "向右箭頭",
+ "geo-style.arrow-up": "向上箭頭",
+ "geo-style.diamond": "菱形 2",
+ "geo-style.ellipse": "橢圓形",
+ "geo-style.hexagon": "六邊形",
+ "geo-style.octagon": "八邊形",
+ "geo-style.oval": "卵形",
+ "geo-style.pentagon": "五邊形",
+ "geo-style.rectangle": "矩形",
+ "geo-style.rhombus": "菱形",
+ "geo-style.rhombus-2": "菱形 2",
+ "geo-style.star": "星形",
+ "geo-style.trapezoid": "梯形",
+ "geo-style.triangle": "三角形",
+ "geo-style.x-box": "X 框",
+ "help-menu.about": "關於",
+ "help-menu.discord": "Discord",
+ "help-menu.github": "GitHub",
+ "help-menu.keyboard-shortcuts": "鍵盤快捷鍵",
+ "help-menu.title": "幫助和資源",
+ "help-menu.twitter": "Twitter",
+ "menu.copy-as": "複製成",
+ "menu.edit": "編輯",
+ "menu.export-as": "匯出成",
+ "menu.file": "檔案",
+ "menu.language": "語言",
+ "menu.preferences": "選項",
+ "menu.title": "選單",
+ "menu.view": "檢視",
+ "navigation-zone.toggle-minimap": "切換小地圖",
+ "navigation-zone.zoom": "縮放",
+ "opacity-style.0.1": "10%",
+ "opacity-style.0.25": "25%",
+ "opacity-style.0.5": "50%",
+ "opacity-style.0.75": "75%",
+ "opacity-style.1": "100%",
+ "page-menu.create-new-page": "建立新頁面",
+ "page-menu.edit-done": "完成",
+ "page-menu.edit-start": "編輯",
+ "page-menu.go-to-page": "轉到頁面",
+ "page-menu.max-page-count-reached": "達到上限頁數",
+ "page-menu.new-page-initial-name": "頁面 1",
+ "page-menu.submenu.delete": "刪除",
+ "page-menu.submenu.duplicate-page": "複製",
+ "page-menu.submenu.move-down": "下移",
+ "page-menu.submenu.move-up": "上移",
+ "page-menu.submenu.rename": "重新命名",
+ "page-menu.submenu.title": "選單",
+ "page-menu.title": "頁面",
+ "people-menu.change-color": "更改顏色",
+ "people-menu.change-name": "更改名稱",
+ "people-menu.follow": "追蹤",
+ "people-menu.invite": "邀請其他人",
+ "people-menu.title": "人員",
+ "people-menu.user": "(您)",
+ "share-menu.copy-link": "複製連結",
+ "share-menu.copy-link-note": "任何人透過連結都能檢視和編輯此專案。",
+ "share-menu.copy-readonly-link": "複製唯讀連結",
+ "share-menu.copy-readonly-link-note": "任何人透過連結都能檢視(但不能編輯)此專案。",
+ "share-menu.create-snapshot-link": "建立快照連結",
+ "share-menu.fork-note": "基於此快照建立新的共享專案。",
+ "share-menu.offline-note": "共享此專案將建立即時副本,並產生新網址。您可以分享網址,與其他至多三十人一起檢視和編輯副本。",
+ "share-menu.project-too-large": "抱歉,無法共享此專案,因為檔案過大。我們已在調整這件事!",
+ "share-menu.readonly-link": "唯讀",
+ "share-menu.save-note": "將此專案作為 .tldr 檔下載到您的電腦。",
+ "share-menu.share-project": "共享此專案",
+ "share-menu.title": "共享",
+ "shortcuts-dialog.edit": "編輯",
+ "shortcuts-dialog.file": "檔案",
+ "shortcuts-dialog.preferences": "選項",
+ "shortcuts-dialog.title": "鍵盤快捷鍵",
+ "shortcuts-dialog.tools": "工具",
+ "shortcuts-dialog.transform": "轉換",
+ "shortcuts-dialog.view": "檢視",
+ "size-style.l": "大",
+ "size-style.m": "中",
+ "size-style.s": "小",
+ "size-style.xl": "特大",
+ "spline-style.cubic": "立方體",
+ "spline-style.line": "直線",
+ "style-panel.align": "對齊",
+ "style-panel.arrowhead-end": "末端",
+ "style-panel.arrowhead-start": "前端",
+ "style-panel.arrowheads": "箭頭",
+ "style-panel.color": "顏色",
+ "style-panel.dash": "虛線",
+ "style-panel.fill": "填充",
+ "style-panel.font": "字型",
+ "style-panel.geo": "形狀",
+ "style-panel.mixed": "混合",
+ "style-panel.opacity": "不透明度",
+ "style-panel.position": "位置",
+ "style-panel.size": "大小",
+ "style-panel.spline": "曲線",
+ "style-panel.title": "樣式",
+ "toast.close": "關閉",
+ "toast.error.copy-fail.desc": "圖片複製失敗",
+ "toast.error.copy-fail.title": "複製失敗",
+ "toast.error.export-fail.desc": "圖片匯出失敗",
+ "toast.error.export-fail.title": "匯出失敗",
+ "tool-panel.drawing": "繪圖",
+ "tool-panel.more": "更多",
+ "tool-panel.shapes": "形狀",
+ "tool.arrow": "箭頭",
+ "tool.arrow-down": "向下箭頭",
+ "tool.arrow-left": "向左箭頭",
+ "tool.arrow-right": "向右箭頭",
+ "tool.arrow-up": "向上箭頭",
+ "tool.asset": "資源",
+ "tool.diamond": "菱形 2",
+ "tool.draw": "手繪",
+ "tool.ellipse": "橢圓形",
+ "tool.embed": "嵌入",
+ "tool.eraser": "橡皮擦",
+ "tool.frame": "框架",
+ "tool.hand": "抓取",
+ "tool.hexagon": "六邊形",
+ "tool.line": "直線",
+ "tool.note": "便利貼",
+ "tool.octagon": "八邊形",
+ "tool.oval": "卵形",
+ "tool.pentagon": "五邊形",
+ "tool.rectangle": "矩形",
+ "tool.rhombus": "菱形",
+ "tool.select": "選取",
+ "tool.star": "星形",
+ "tool.text": "文字",
+ "tool.trapezoid": "梯形",
+ "tool.triangle": "三角形",
+ "tool.x-box": "X 框",
+ "vscode.file-open.backup": "備份",
+ "vscode.file-open.backup-failed": "備份失敗:這不是 .tldr 檔。",
+ "vscode.file-open.backup-saved": "已備份",
+ "vscode.file-open.desc": "此檔案由較早版本的 tldraw 所建立。您想要更新它以用於新版本上嗎?",
+ "vscode.file-open.dont-show-again": "不再詢問",
+ "vscode.file-open.open": "繼續"
+}
diff --git a/apps/web/app/helpers/server/auth.ts b/apps/web/server/auth.ts
index 73119d87..c4e426d4 100644
--- a/apps/web/app/helpers/server/auth.ts
+++ b/apps/web/server/auth.ts
@@ -2,6 +2,7 @@ import NextAuth, { NextAuthResult } from "next-auth";
import Google from "next-auth/providers/google";
import { DrizzleAdapter } from "@auth/drizzle-adapter";
import { db } from "./db";
+import { accounts, sessions, users, verificationTokens } from "./db/schema";
export const {
handlers: { GET, POST },
@@ -10,16 +11,20 @@ export const {
auth,
} = NextAuth({
secret: process.env.BACKEND_SECURITY_KEY,
- callbacks: {
- session: ({ session, token, user }) => ({
- ...session,
- user: {
- ...session.user,
- id: user.id,
- },
- }),
- },
- adapter: DrizzleAdapter(db),
+ // callbacks: {
+ // session: ({ session, token, user }) => ({
+ // ...session,
+ // user: {
+ // ...session.user,
+ // },
+ // }),
+ // },
+ adapter: DrizzleAdapter(db, {
+ usersTable: users,
+ accountsTable: accounts,
+ sessionsTable: sessions,
+ verificationTokensTable: verificationTokens,
+ }),
providers: [
Google({
clientId: process.env.GOOGLE_CLIENT_ID,
diff --git a/apps/web/app/helpers/server/db/index.ts b/apps/web/server/db/index.ts
index 4d671bea..4d671bea 100644
--- a/apps/web/app/helpers/server/db/index.ts
+++ b/apps/web/server/db/index.ts
diff --git a/apps/web/app/helpers/server/db/schema.ts b/apps/web/server/db/schema.ts
index c4616eb2..1ff23c82 100644
--- a/apps/web/app/helpers/server/db/schema.ts
+++ b/apps/web/server/db/schema.ts
@@ -7,75 +7,88 @@ import {
text,
integer,
} from "drizzle-orm/sqlite-core";
+import type { AdapterAccountType } from "next-auth/adapters";
export const createTable = sqliteTableCreator((name) => `${name}`);
export const users = createTable("user", {
- id: text("id", { length: 255 }).notNull().primaryKey(),
- name: text("name", { length: 255 }),
- email: text("email", { length: 255 }).notNull(),
- emailVerified: int("emailVerified", { mode: "timestamp" }).default(
- sql`CURRENT_TIMESTAMP`,
- ),
- image: text("image", { length: 255 }),
+ id: text("id")
+ .primaryKey()
+ .$defaultFn(() => crypto.randomUUID()),
+ name: text("name"),
+ email: text("email").notNull(),
+ emailVerified: integer("emailVerified", { mode: "timestamp_ms" }),
+ image: text("image"),
});
export type User = typeof users.$inferSelect;
-export const usersRelations = relations(users, ({ many }) => ({
- accounts: many(accounts),
- sessions: many(sessions),
-}));
-
export const accounts = createTable(
"account",
{
- id: integer("id").notNull().primaryKey({ autoIncrement: true }),
- userId: text("userId", { length: 255 })
+ userId: text("userId")
.notNull()
.references(() => users.id, { onDelete: "cascade" }),
- type: text("type", { length: 255 }).notNull(),
- provider: text("provider", { length: 255 }).notNull(),
- providerAccountId: text("providerAccountId", { length: 255 }).notNull(),
+ type: text("type").$type<AdapterAccountType>().notNull(),
+ provider: text("provider").notNull(),
+ providerAccountId: text("providerAccountId").notNull(),
refresh_token: text("refresh_token"),
access_token: text("access_token"),
- expires_at: int("expires_at"),
- token_type: text("token_type", { length: 255 }),
- scope: text("scope", { length: 255 }),
+ expires_at: integer("expires_at"),
+ token_type: text("token_type"),
+ scope: text("scope"),
id_token: text("id_token"),
- session_state: text("session_state", { length: 255 }),
- oauth_token_secret: text("oauth_token_secret"),
- oauth_token: text("oauth_token"),
+ session_state: text("session_state"),
},
(account) => ({
- userIdIdx: index("account_userId_idx").on(account.userId),
+ compoundKey: primaryKey({
+ columns: [account.provider, account.providerAccountId],
+ }),
}),
);
-export const sessions = createTable(
- "session",
+export const sessions = createTable("session", {
+ sessionToken: text("sessionToken").primaryKey(),
+ userId: text("userId")
+ .notNull()
+ .references(() => users.id, { onDelete: "cascade" }),
+ expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
+});
+
+export const verificationTokens = createTable(
+ "verificationToken",
{
- id: integer("id").notNull().primaryKey({ autoIncrement: true }),
- sessionToken: text("sessionToken", { length: 255 }).notNull(),
- userId: text("userId", { length: 255 })
- .notNull()
- .references(() => users.id, { onDelete: "cascade" }),
- expires: int("expires", { mode: "timestamp" }).notNull(),
+ identifier: text("identifier").notNull(),
+ token: text("token").notNull(),
+ expires: integer("expires", { mode: "timestamp_ms" }).notNull(),
},
- (session) => ({
- userIdIdx: index("session_userId_idx").on(session.userId),
+ (verificationToken) => ({
+ compositePk: primaryKey({
+ columns: [verificationToken.identifier, verificationToken.token],
+ }),
}),
);
-export const verificationTokens = createTable(
- "verificationToken",
+export const authenticators = createTable(
+ "authenticator",
{
- identifier: text("identifier", { length: 255 }).notNull(),
- token: text("token", { length: 255 }).notNull(),
- expires: int("expires", { mode: "timestamp" }).notNull(),
+ credentialID: text("credentialID").notNull().unique(),
+ userId: text("userId")
+ .notNull()
+ .references(() => users.id, { onDelete: "cascade" }),
+ providerAccountId: text("providerAccountId").notNull(),
+ credentialPublicKey: text("credentialPublicKey").notNull(),
+ counter: integer("counter").notNull(),
+ credentialDeviceType: text("credentialDeviceType").notNull(),
+ credentialBackedUp: integer("credentialBackedUp", {
+ mode: "boolean",
+ }).notNull(),
+ transports: text("transports"),
},
- (vt) => ({
- compoundKey: primaryKey({ columns: [vt.identifier, vt.token] }),
+ (authenticator) => ({
+ compositePK: primaryKey({
+ columns: [authenticator.userId, authenticator.credentialID],
+ }),
}),
);
@@ -90,11 +103,9 @@ export const storedContent = createTable(
savedAt: int("savedAt", { mode: "timestamp" }).notNull(),
baseUrl: text("baseUrl", { length: 255 }),
ogImage: text("ogImage", { length: 255 }),
- type: text("type", { enum: ["note", "page", "twitter-bookmark"] }).default(
- "page",
- ),
+ type: text("type").default("page"),
image: text("image", { length: 255 }),
- user: text("user", { length: 255 }).references(() => users.id, {
+ userId: text("user").references(() => users.id, {
onDelete: "cascade",
}),
},
@@ -102,10 +113,12 @@ export const storedContent = createTable(
urlIdx: index("storedContent_url_idx").on(sc.url),
savedAtIdx: index("storedContent_savedAt_idx").on(sc.savedAt),
titleInx: index("storedContent_title_idx").on(sc.title),
- userIdx: index("storedContent_user_idx").on(sc.user),
+ userIdx: index("storedContent_user_idx").on(sc.userId),
}),
);
+export type Content = typeof storedContent.$inferSelect;
+
export const contentToSpace = createTable(
"contentToSpace",
{
diff --git a/package.json b/package.json
index e891d513..7ceb7192 100644
--- a/package.json
+++ b/package.json
@@ -12,9 +12,8 @@
"devDependencies": {
"@clack/prompts": "^0.7.0",
"@cloudflare/next-on-pages": "1",
- "@cloudflare/workers-types": "^4.20240512.0",
+ "@cloudflare/workers-types": "^4.20240614.0",
"@repo/eslint-config": "*",
- "@repo/shared-types": "*",
"@repo/tailwind-config": "*",
"@repo/typescript-config": "*",
"@repo/ui": "*",
@@ -28,7 +27,7 @@
"readline-sync": "^1.4.10",
"tailwindcss": "^3.4.3",
"tailwindcss-animate": "^1.0.7",
- "turbo": "latest",
+ "turbo": "2.0.3",
"vercel": "^34.2.0"
},
"engines": {
@@ -46,32 +45,48 @@
"@auth/drizzle-adapter": "^1.1.0",
"@aws-sdk/client-s3": "^3.577.0",
"@aws-sdk/s3-request-presigner": "^3.577.0",
- "@cloudflare/puppeteer": "^0.0.8",
+ "@cloudflare/puppeteer": "^0.0.11",
"@headlessui/react": "^2.0.4",
+ "@hono/swagger-ui": "^0.2.2",
"@hookform/resolvers": "^3.4.2",
"@iarna/toml": "^2.2.5",
"@langchain/cloudflare": "^0.0.6",
"@million/lint": "^1.0.0-rc.18",
+ "@radix-ui/react-accordion": "^1.1.2",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-label": "^2.0.2",
"@radix-ui/react-progress": "^1.0.3",
"@radix-ui/react-scroll-area": "^1.0.5",
+ "@radix-ui/react-select": "^2.0.0",
+ "@radix-ui/react-separator": "^1.0.3",
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-toast": "^1.1.5",
+ "@tldraw/assets": "^2.2.0",
"@types/readline-sync": "^1.4.8",
"ai": "^3.1.14",
+ "aws4fetch": "^1.0.18",
"cheerio": "^1.0.0-rc.12",
"compromise": "^14.13.0",
"drizzle-orm": "^0.30.10",
"framer-motion": "^11.2.6",
+ "katex": "^0.16.10",
"lucide-react": "^0.379.0",
"next-app-theme": "^0.1.10",
"next-auth": "^5.0.0-beta.18",
+ "next-themes": "^0.3.0",
"random-js": "^2.1.0",
"react-dropzone": "^14.2.3",
"react-hook-form": "^7.51.5",
- "sonner": "^1.4.41",
+ "react-markdown": "^9.0.1",
+ "react-tweet": "^3.2.1",
+ "rehype-highlight": "^7.0.0",
+ "rehype-katex": "^7.0.0",
+ "remark-gfm": "^4.0.0",
+ "remark-math": "^6.0.0",
+ "sonner": "^1.5.0",
+ "tailwind-scrollbar": "^3.1.0",
+ "tldraw": "^2.1.4",
"uploadthing": "^6.10.4",
"zod": "^3.23.8"
},
diff --git a/packages/shared-types/index.ts b/packages/shared-types/index.ts
index bf4a56da..b8792369 100644
--- a/packages/shared-types/index.ts
+++ b/packages/shared-types/index.ts
@@ -1,7 +1,55 @@
-export type ChatHistory = {
- question: string;
- answer: {
- parts: { text: string }[];
- sources: { isNote: boolean; source: string }[];
- };
-};
+import { z } from "zod";
+
+export const ChatHistoryZod = z.object({
+ question: z.string(),
+ answer: z.object({
+ parts: z.array(z.object({ text: z.string() })),
+ sources: z.array(
+ z.object({
+ type: z.enum(["note", "page", "tweet"]),
+ source: z.string(),
+ title: z.string(),
+ content: z.string(),
+ numChunks: z.number().optional().default(1),
+ }),
+ ),
+ }),
+});
+
+export type ChatHistory = z.infer<typeof ChatHistoryZod>;
+
+export const ModelCompatibleChatHistoryZod = z.array(
+ z.object({
+ role: z.union([
+ z.literal("user"),
+ z.literal("assistant"),
+ z.literal("system"),
+ ]),
+ content: z.string(),
+ }),
+);
+
+export type ModelCompatibleChatHistory = z.infer<
+ typeof ModelCompatibleChatHistoryZod
+>;
+
+export function convertChatHistoryList(
+ chatHistoryList: ChatHistory[],
+): ModelCompatibleChatHistory {
+ let convertedChats: ModelCompatibleChatHistory = [];
+
+ chatHistoryList.forEach((chat) => {
+ convertedChats.push(
+ {
+ role: "user",
+ content: chat.question,
+ },
+ {
+ role: "assistant",
+ content: chat.answer.parts.map((part) => part.text).join(" "),
+ },
+ );
+ });
+
+ return convertedChats;
+}
diff --git a/packages/tailwind-config/globals.css b/packages/tailwind-config/globals.css
index 32137ef7..43bdf26e 100644
--- a/packages/tailwind-config/globals.css
+++ b/packages/tailwind-config/globals.css
@@ -50,6 +50,42 @@ body {
align-items: center;
justify-content: center;
}
+
+ .markdown table {
+ --tw-border-spacing-x: 0px;
+ --tw-border-spacing-y: 0px;
+ border-collapse: separate;
+ border-spacing: var(--tw-border-spacing-x) var(--tw-border-spacing-y);
+ width: 100%;
+ }
+ .markdown th {
+ background-color: rgba(236, 236, 241, 0.2);
+ border-bottom-width: 1px;
+ border-left-width: 1px;
+ border-top-width: 1px;
+ padding: 0.25rem 0.75rem;
+ }
+ .markdown th:first-child {
+ border-top-left-radius: 0.375rem;
+ }
+ .markdown th:last-child {
+ border-right-width: 1px;
+ border-top-right-radius: 0.375rem;
+ }
+ .markdown td {
+ border-bottom-width: 1px;
+ border-left-width: 1px;
+ padding: 0.25rem 0.75rem;
+ }
+ .markdown td:last-child {
+ border-right-width: 1px;
+ }
+ .markdown tbody tr:last-child td:first-child {
+ border-bottom-left-radius: 0.375rem;
+ }
+ .markdown tbody tr:last-child td:last-child {
+ border-bottom-right-radius: 0.375rem;
+ }
}
@layer utilities {
@@ -58,6 +94,34 @@ body {
}
}
+@layer components {
+ .markdown ol,
+ .markdown ul {
+ display: flex;
+ flex-direction: column;
+ padding-left: 1rem;
+ }
+
+ .markdown ol li,
+ .markdown ol li > p,
+ .markdown ol ol,
+ .markdown ol ul,
+ .markdown ul li,
+ .markdown ul li > p,
+ .markdown ul ol,
+ .markdown ul ul {
+ margin: 0;
+ }
+
+ .markdown ul li:before {
+ content: "•";
+ font-size: 0.875rem;
+ line-height: 1.25rem;
+ margin-left: -1rem;
+ position: absolute;
+ }
+}
+
.gradient-background {
background: linear-gradient(
150deg,
@@ -85,3 +149,62 @@ body {
::-webkit-scrollbar-thumb:hover {
background: #22303d;
}
+
+.no-scrollbar {
+ /* For WebKit (Safari, Chrome, etc.) */
+ &::-webkit-scrollbar {
+ display: none;
+ }
+
+ /* For Firefox */
+ scrollbar-width: none;
+
+ /* For IE and Edge */
+ -ms-overflow-style: none;
+}
+
+:not(pre) > code.hljs,
+:not(pre) > code[class*="language-"] {
+ border-radius: 0.3em;
+ white-space: normal;
+}
+.hljs-comment {
+ color: hsla(0, 0%, 100%, 0.5);
+}
+.hljs-meta {
+ color: hsla(0, 0%, 100%, 0.6);
+}
+.hljs-built_in,
+.hljs-class .hljs-title {
+ color: #e9950c;
+}
+.hljs-doctag,
+.hljs-formula,
+.hljs-keyword,
+.hljs-literal {
+ color: #2e95d3;
+}
+.hljs-addition,
+.hljs-attribute,
+.hljs-meta-string,
+.hljs-regexp,
+.hljs-string {
+ color: #00a67d;
+}
+.hljs-attr,
+.hljs-number,
+.hljs-selector-attr,
+.hljs-selector-class,
+.hljs-selector-pseudo,
+.hljs-template-variable,
+.hljs-type,
+.hljs-variable {
+ color: #df3079;
+}
+.hljs-bullet,
+.hljs-link,
+.hljs-selector-id,
+.hljs-symbol,
+.hljs-title {
+ color: #f22c3d;
+}
diff --git a/packages/tailwind-config/tailwind.config.ts b/packages/tailwind-config/tailwind.config.ts
index bf36b528..3711bd41 100644
--- a/packages/tailwind-config/tailwind.config.ts
+++ b/packages/tailwind-config/tailwind.config.ts
@@ -71,7 +71,11 @@ const config = {
},
},
},
- plugins: [require("tailwindcss-animate"), require("@tailwindcss/typography")],
+ plugins: [
+ require("tailwindcss-animate"),
+ require("@tailwindcss/typography"),
+ require("tailwind-scrollbar"),
+ ],
} satisfies Config;
export default config;
diff --git a/packages/ui/components/icons.tsx b/packages/ui/components/icons.tsx
index d72c8e1f..134a2a96 100644
--- a/packages/ui/components/icons.tsx
+++ b/packages/ui/components/icons.tsx
@@ -277,8 +277,8 @@ export const X = (props: SVGProps<SVGSVGElement>) => (
export const Google = (props: SVGProps<SVGSVGElement>) => (
<svg
- width="1em"
- height="1em"
+ width="1.5em"
+ height="1.5em"
viewBox="0 0 256 262"
xmlns="http://www.w3.org/2000/svg"
preserveAspectRatio="xMidYMid"
diff --git a/packages/ui/icons/index.ts b/packages/ui/icons/index.ts
index 6c528aba..7788c20f 100644
--- a/packages/ui/icons/index.ts
+++ b/packages/ui/icons/index.ts
@@ -3,8 +3,11 @@ import ChatIcon from "./chat.svg";
import HistoryIcon from "./history.svg";
import ExploreIcon from "./explore.svg";
import MemoriesIcon from "./memories.svg";
-import ArrowRightIcon from "./arrowRight.svg";
+import ArrowRightIcon from "./arrowright.svg";
import SelectIcon from "./select.svg";
+import SearchIcon from "./search.svg";
+import NextIcon from "./nextarrow.svg";
+import UrlIcon from "./url.svg";
export {
AddIcon,
@@ -14,4 +17,7 @@ export {
MemoriesIcon,
ArrowRightIcon,
SelectIcon,
+ SearchIcon,
+ NextIcon,
+ UrlIcon
};
diff --git a/packages/ui/icons/nextarrow.svg b/packages/ui/icons/nextarrow.svg
new file mode 100644
index 00000000..b0f4a5fe
--- /dev/null
+++ b/packages/ui/icons/nextarrow.svg
@@ -0,0 +1,3 @@
+<svg width="8" height="14" viewBox="0 0 8 14" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M1.1875 1.375L6.8125 7L1.1875 12.625" stroke="#B3BCC5" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/packages/ui/icons/search.svg b/packages/ui/icons/search.svg
new file mode 100644
index 00000000..af8b6664
--- /dev/null
+++ b/packages/ui/icons/search.svg
@@ -0,0 +1,3 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M14.75 14.7501L10.8523 10.8523M10.8523 10.8523C11.9072 9.79742 12.4998 8.36662 12.4998 6.87472C12.4998 5.38282 11.9072 3.95203 10.8523 2.8971C9.79732 1.84217 8.36653 1.24951 6.87463 1.24951C5.38273 1.24951 3.95194 1.84217 2.89701 2.8971C1.84207 3.95203 1.24942 5.38282 1.24942 6.87472C1.24942 8.36662 1.84207 9.79742 2.89701 10.8523C3.95194 11.9073 5.38273 12.4999 6.87463 12.4999C8.36653 12.4999 9.79732 11.9073 10.8523 10.8523Z" stroke="#687077" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
diff --git a/packages/ui/icons/url.svg b/packages/ui/icons/url.svg
new file mode 100644
index 00000000..65084459
--- /dev/null
+++ b/packages/ui/icons/url.svg
@@ -0,0 +1,4 @@
+<svg width="18" height="18" viewBox="0 0 18 18" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M9.89249 6.51602C10.3799 6.74871 10.8043 7.09497 11.1301 7.5257C11.4559 7.95643 11.6735 8.45906 11.7648 8.99136C11.8561 9.52367 11.8183 10.0701 11.6546 10.5848C11.4908 11.0994 11.206 11.5673 10.824 11.949L7.44899 15.324C6.81605 15.957 5.9576 16.3125 5.06249 16.3125C4.16738 16.3125 3.30893 15.957 2.67599 15.324C2.04305 14.6911 1.68747 13.8326 1.68747 12.9375C1.68747 12.0424 2.04305 11.184 2.67599 10.551L3.99374 9.23327M14.0062 8.76677L15.324 7.44902C15.9569 6.81608 16.3125 5.95763 16.3125 5.06252C16.3125 4.16741 15.9569 3.30896 15.324 2.67602C14.6911 2.04308 13.8326 1.6875 12.9375 1.6875C12.0424 1.6875 11.1839 2.04308 10.551 2.67602L7.17599 6.05102C6.79397 6.43277 6.50914 6.90063 6.34543 7.41529C6.18172 7.92995 6.1439 8.47638 6.23517 9.00868C6.32643 9.54098 6.54411 10.0436 6.86991 10.4743C7.19571 10.9051 7.62012 11.2513 8.10749 11.484" stroke="#B3BCC5" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+</svg>
+
diff --git a/packages/ui/shadcn/accordion.tsx b/packages/ui/shadcn/accordion.tsx
new file mode 100644
index 00000000..a5dedb19
--- /dev/null
+++ b/packages/ui/shadcn/accordion.tsx
@@ -0,0 +1,54 @@
+"use client";
+
+import * as React from "react";
+import * as AccordionPrimitive from "@radix-ui/react-accordion";
+import { ChevronDown } from "lucide-react";
+
+import { cn } from "@repo/ui/lib/utils";
+
+const Accordion = AccordionPrimitive.Root;
+
+const AccordionItem = React.forwardRef<
+ React.ElementRef<typeof AccordionPrimitive.Item>,
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Item>
+>(({ className, ...props }, ref) => (
+ <AccordionPrimitive.Item ref={ref} className={cn(className)} {...props} />
+));
+AccordionItem.displayName = "AccordionItem";
+
+const AccordionTrigger = React.forwardRef<
+ React.ElementRef<typeof AccordionPrimitive.Trigger>,
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Trigger>
+>(({ className, children, ...props }, ref) => (
+ <AccordionPrimitive.Header className="flex">
+ <AccordionPrimitive.Trigger
+ ref={ref}
+ className={cn(
+ "flex flex-1 items-center gap-2 py-4 font-medium transition-all hover:underline [&[data-state=open]>svg]:rotate-180",
+ className,
+ )}
+ {...props}
+ >
+ {children}
+ <ChevronDown className="h-4 w-4 shrink-0 transition-transform duration-200" />
+ </AccordionPrimitive.Trigger>
+ </AccordionPrimitive.Header>
+));
+AccordionTrigger.displayName = AccordionPrimitive.Trigger.displayName;
+
+const AccordionContent = React.forwardRef<
+ React.ElementRef<typeof AccordionPrimitive.Content>,
+ React.ComponentPropsWithoutRef<typeof AccordionPrimitive.Content>
+>(({ className, children, ...props }, ref) => (
+ <AccordionPrimitive.Content
+ ref={ref}
+ className="overflow-hidden text-sm transition-all data-[state=closed]:animate-accordion-up data-[state=open]:animate-accordion-down"
+ {...props}
+ >
+ <div className={cn("pb-4 pt-0", className)}>{children}</div>
+ </AccordionPrimitive.Content>
+));
+
+AccordionContent.displayName = AccordionPrimitive.Content.displayName;
+
+export { Accordion, AccordionItem, AccordionTrigger, AccordionContent };
diff --git a/packages/ui/shadcn/select.tsx b/packages/ui/shadcn/select.tsx
new file mode 100644
index 00000000..8abe27c1
--- /dev/null
+++ b/packages/ui/shadcn/select.tsx
@@ -0,0 +1,160 @@
+"use client";
+
+import * as React from "react";
+import * as SelectPrimitive from "@radix-ui/react-select";
+import { Check, ChevronDown, ChevronUp } from "lucide-react";
+
+import { cn } from "@repo/ui/lib/utils";
+
+const Select = SelectPrimitive.Root;
+
+const SelectGroup = SelectPrimitive.Group;
+
+const SelectValue = SelectPrimitive.Value;
+
+const SelectTrigger = React.forwardRef<
+ React.ElementRef<typeof SelectPrimitive.Trigger>,
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Trigger>
+>(({ className, children, ...props }, ref) => (
+ <SelectPrimitive.Trigger
+ ref={ref}
+ className={cn(
+ "flex h-10 w-full items-center justify-between rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus:outline-none focus:ring-2 focus:ring-ring focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 [&>span]:line-clamp-1",
+ className,
+ )}
+ {...props}
+ >
+ {children}
+ <SelectPrimitive.Icon asChild>
+ <ChevronDown className="h-4 w-4 opacity-50" />
+ </SelectPrimitive.Icon>
+ </SelectPrimitive.Trigger>
+));
+SelectTrigger.displayName = SelectPrimitive.Trigger.displayName;
+
+const SelectScrollUpButton = React.forwardRef<
+ React.ElementRef<typeof SelectPrimitive.ScrollUpButton>,
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollUpButton>
+>(({ className, ...props }, ref) => (
+ <SelectPrimitive.ScrollUpButton
+ ref={ref}
+ className={cn(
+ "flex cursor-default items-center justify-center py-1",
+ className,
+ )}
+ {...props}
+ >
+ <ChevronUp className="h-4 w-4" />
+ </SelectPrimitive.ScrollUpButton>
+));
+SelectScrollUpButton.displayName = SelectPrimitive.ScrollUpButton.displayName;
+
+const SelectScrollDownButton = React.forwardRef<
+ React.ElementRef<typeof SelectPrimitive.ScrollDownButton>,
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.ScrollDownButton>
+>(({ className, ...props }, ref) => (
+ <SelectPrimitive.ScrollDownButton
+ ref={ref}
+ className={cn(
+ "flex cursor-default items-center justify-center py-1",
+ className,
+ )}
+ {...props}
+ >
+ <ChevronDown className="h-4 w-4" />
+ </SelectPrimitive.ScrollDownButton>
+));
+SelectScrollDownButton.displayName =
+ SelectPrimitive.ScrollDownButton.displayName;
+
+const SelectContent = React.forwardRef<
+ React.ElementRef<typeof SelectPrimitive.Content>,
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Content>
+>(({ className, children, position = "popper", ...props }, ref) => (
+ <SelectPrimitive.Portal>
+ <SelectPrimitive.Content
+ ref={ref}
+ className={cn(
+ "relative z-50 max-h-96 min-w-[8rem] overflow-hidden rounded-md text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2",
+ position === "popper" &&
+ "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
+ className,
+ )}
+ position={position}
+ {...props}
+ >
+ <SelectScrollUpButton />
+ <SelectPrimitive.Viewport
+ className={cn(
+ "p-1",
+ position === "popper" &&
+ "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)]",
+ )}
+ >
+ {children}
+ </SelectPrimitive.Viewport>
+ <SelectScrollDownButton />
+ </SelectPrimitive.Content>
+ </SelectPrimitive.Portal>
+));
+SelectContent.displayName = SelectPrimitive.Content.displayName;
+
+const SelectLabel = React.forwardRef<
+ React.ElementRef<typeof SelectPrimitive.Label>,
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Label>
+>(({ className, ...props }, ref) => (
+ <SelectPrimitive.Label
+ ref={ref}
+ className={cn("py-1.5 pl-8 pr-2 text-sm font-semibold", className)}
+ {...props}
+ />
+));
+SelectLabel.displayName = SelectPrimitive.Label.displayName;
+
+const SelectItem = React.forwardRef<
+ React.ElementRef<typeof SelectPrimitive.Item>,
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Item>
+>(({ className, children, ...props }, ref) => (
+ <SelectPrimitive.Item
+ ref={ref}
+ className={cn(
+ "relative flex w-full cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 text-sm outline-none focus:bg-foreground-menu data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
+ className,
+ )}
+ {...props}
+ >
+ <span className="absolute left-2 flex h-3.5 w-3.5 items-center justify-center">
+ <SelectPrimitive.ItemIndicator>
+ <Check className="h-4 w-4" />
+ </SelectPrimitive.ItemIndicator>
+ </span>
+
+ <SelectPrimitive.ItemText>{children}</SelectPrimitive.ItemText>
+ </SelectPrimitive.Item>
+));
+SelectItem.displayName = SelectPrimitive.Item.displayName;
+
+const SelectSeparator = React.forwardRef<
+ React.ElementRef<typeof SelectPrimitive.Separator>,
+ React.ComponentPropsWithoutRef<typeof SelectPrimitive.Separator>
+>(({ className, ...props }, ref) => (
+ <SelectPrimitive.Separator
+ ref={ref}
+ className={cn("-mx-1 my-1 h-px bg-muted", className)}
+ {...props}
+ />
+));
+SelectSeparator.displayName = SelectPrimitive.Separator.displayName;
+
+export {
+ Select,
+ SelectGroup,
+ SelectValue,
+ SelectTrigger,
+ SelectContent,
+ SelectLabel,
+ SelectItem,
+ SelectSeparator,
+ SelectScrollUpButton,
+ SelectScrollDownButton,
+};
diff --git a/packages/ui/shadcn/separator.tsx b/packages/ui/shadcn/separator.tsx
new file mode 100644
index 00000000..d956f2f0
--- /dev/null
+++ b/packages/ui/shadcn/separator.tsx
@@ -0,0 +1,21 @@
+"use client";
+
+import * as React from "react";
+import * as SeparatorPrimitive from "@radix-ui/react-separator";
+import { cn } from "@repo/ui/lib/utils";
+
+const Separator = React.forwardRef<
+ React.ElementRef<typeof SeparatorPrimitive.Root>,
+ React.ComponentPropsWithoutRef<typeof SeparatorPrimitive.Root>
+>(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => (
+ <SeparatorPrimitive.Root
+ ref={ref}
+ decorative={decorative}
+ orientation={orientation}
+ className={cn("shrink-0 bg-border", orientation === "horizontal" ? "h-[1px] w-full" : " w-[1px]", className)}
+ {...props}
+ />
+));
+Separator.displayName = SeparatorPrimitive.Root.displayName;
+
+export { Separator };
diff --git a/packages/ui/shadcn/sonner.tsx b/packages/ui/shadcn/sonner.tsx
new file mode 100644
index 00000000..549cf841
--- /dev/null
+++ b/packages/ui/shadcn/sonner.tsx
@@ -0,0 +1,31 @@
+"use client";
+
+import { useTheme } from "next-themes";
+import { Toaster as Sonner } from "sonner";
+
+type ToasterProps = React.ComponentProps<typeof Sonner>;
+
+const Toaster = ({ ...props }: ToasterProps) => {
+ const { theme = "system" } = useTheme();
+
+ return (
+ <Sonner
+ theme={theme as ToasterProps["theme"]}
+ className="toaster group"
+ toastOptions={{
+ classNames: {
+ toast:
+ "group toast group-[.toaster]:bg-background group-[.toaster]:text-foreground group-[.toaster]:border-border group-[.toaster]:shadow-lg",
+ description: "group-[.toast]:text-muted-foreground",
+ actionButton:
+ "group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
+ cancelButton:
+ "group-[.toast]:bg-muted group-[.toast]:text-muted-foreground",
+ },
+ }}
+ {...props}
+ />
+ );
+};
+
+export { Toaster };
diff --git a/packages/ui/shadcn/tabs.tsx b/packages/ui/shadcn/tabs.tsx
index aad81ef8..75d8d2ee 100644
--- a/packages/ui/shadcn/tabs.tsx
+++ b/packages/ui/shadcn/tabs.tsx
@@ -1,3 +1,4 @@
+<<<<<<< HEAD
"use client";
import * as React from "react";
@@ -6,6 +7,16 @@ import * as TabsPrimitive from "@radix-ui/react-tabs";
import { cn } from "@repo/ui/lib/utils";
const Tabs = TabsPrimitive.Root;
+=======
+"use client"
+
+import * as React from "react"
+import * as TabsPrimitive from "@radix-ui/react-tabs"
+
+import { cn } from "@repo/ui/lib/utils";
+
+const Tabs = TabsPrimitive.Root
+>>>>>>> origin/codetorso
const TabsList = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.List>,
@@ -14,6 +25,7 @@ const TabsList = React.forwardRef<
<TabsPrimitive.List
ref={ref}
className={cn(
+<<<<<<< HEAD
"inline-flex h-10 items-center justify-center rounded-md p-1 text-muted-foreground",
className,
)}
@@ -21,6 +33,15 @@ const TabsList = React.forwardRef<
/>
));
TabsList.displayName = TabsPrimitive.List.displayName;
+=======
+ "inline-flex h-10 items-center justify-center rounded-md bg-muted p-1 text-muted-foreground",
+ className
+ )}
+ {...props}
+ />
+))
+TabsList.displayName = TabsPrimitive.List.displayName
+>>>>>>> origin/codetorso
const TabsTrigger = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Trigger>,
@@ -29,6 +50,7 @@ const TabsTrigger = React.forwardRef<
<TabsPrimitive.Trigger
ref={ref}
className={cn(
+<<<<<<< HEAD
"inline-flex items-center justify-center whitespace-nowrap rounded-sm px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-background data-[state=active]:text-foreground data-[state=active]:shadow-sm",
className,
)}
@@ -36,6 +58,15 @@ const TabsTrigger = React.forwardRef<
/>
));
TabsTrigger.displayName = TabsPrimitive.Trigger.displayName;
+=======
+ "inline-flex items-center justify-center whitespace-nowrap rounded-xl px-3 py-1.5 text-sm font-medium ring-offset-background transition-all focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:pointer-events-none disabled:opacity-50 data-[state=active]:bg-[#2B3237] data-[state=active]:text-[#858B92] data-[state=active]:shadow-sm",
+ className
+ )}
+ {...props}
+ />
+))
+TabsTrigger.displayName = TabsPrimitive.Trigger.displayName
+>>>>>>> origin/codetorso
const TabsContent = React.forwardRef<
React.ElementRef<typeof TabsPrimitive.Content>,
@@ -45,6 +76,7 @@ const TabsContent = React.forwardRef<
ref={ref}
className={cn(
"mt-2 ring-offset-background focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2",
+<<<<<<< HEAD
className,
)}
{...props}
@@ -53,3 +85,13 @@ const TabsContent = React.forwardRef<
TabsContent.displayName = TabsPrimitive.Content.displayName;
export { Tabs, TabsList, TabsTrigger, TabsContent };
+=======
+ className
+ )}
+ {...props}
+ />
+))
+TabsContent.displayName = TabsPrimitive.Content.displayName
+
+export { Tabs, TabsList, TabsTrigger, TabsContent }
+>>>>>>> origin/codetorso
diff --git a/packages/ui/shadcn/textarea.tsx b/packages/ui/shadcn/textarea.tsx
new file mode 100644
index 00000000..30ce2800
--- /dev/null
+++ b/packages/ui/shadcn/textarea.tsx
@@ -0,0 +1,24 @@
+import * as React from "react"
+
+import { cn } from "@repo/ui/lib/utils";
+
+export interface TextareaProps
+ extends React.TextareaHTMLAttributes<HTMLTextAreaElement> {}
+
+const Textarea = React.forwardRef<HTMLTextAreaElement, TextareaProps>(
+ ({ className, ...props }, ref) => {
+ return (
+ <textarea
+ className={cn(
+ "flex min-h-[80px] w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50",
+ className
+ )}
+ ref={ref}
+ {...props}
+ />
+ )
+ }
+)
+Textarea.displayName = "Textarea"
+
+export { Textarea }
diff --git a/turbo.json b/turbo.json
index 08f5cb2e..0c71b603 100644
--- a/turbo.json
+++ b/turbo.json
@@ -1,7 +1,7 @@
{
"$schema": "https://turbo.build/schema.json",
"globalDependencies": ["**/.env.*local"],
- "pipeline": {
+ "tasks": {
"build": {
"dependsOn": ["^build"],
"outputs": [".next/**", "!.next/cache/**"]