├── apps ├── web │ ├── turso │ │ └── .gitkeep │ ├── .npmrc │ ├── src │ │ ├── lib │ │ │ ├── types.d.ts │ │ │ ├── components │ │ │ │ ├── app │ │ │ │ │ ├── groups │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── connect │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── command │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── command-list-groups.svelte │ │ │ │ │ ├── saves │ │ │ │ │ │ ├── item │ │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ │ ├── item-description.svelte │ │ │ │ │ │ │ ├── item-media.svelte │ │ │ │ │ │ │ ├── types │ │ │ │ │ │ │ │ ├── item-color.svelte │ │ │ │ │ │ │ │ ├── item-text.svelte │ │ │ │ │ │ │ │ ├── item-image.svelte │ │ │ │ │ │ │ │ └── item-website.svelte │ │ │ │ │ │ │ └── item-image.svelte │ │ │ │ │ │ └── index.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── nav │ │ │ │ │ │ ├── index.ts │ │ │ │ │ │ └── link.svelte │ │ │ │ │ ├── gradient.svelte │ │ │ │ │ ├── empty-state.svelte │ │ │ │ │ └── copy-button.svelte │ │ │ │ └── site │ │ │ │ │ ├── hero │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── vault-clip-path.svelte │ │ │ │ │ └── context.ts │ │ │ │ │ ├── index.ts │ │ │ │ │ └── logos │ │ │ │ │ ├── twitter-bookmark.svelte │ │ │ │ │ ├── github-star.svelte │ │ │ │ │ ├── codepen.svelte │ │ │ │ │ ├── index.ts │ │ │ │ │ ├── text.svelte │ │ │ │ │ └── colors.svelte │ │ │ ├── index.ts │ │ │ ├── actions │ │ │ │ └── index.ts │ │ │ ├── server │ │ │ │ └── db │ │ │ │ │ ├── index.ts │ │ │ │ │ └── migrate.ts │ │ │ └── stores │ │ │ │ ├── index.ts │ │ │ │ └── new-stash.ts │ │ ├── routes │ │ │ ├── homepage │ │ │ │ └── +server.ts │ │ │ ├── main │ │ │ │ ├── groups │ │ │ │ │ ├── +page.svelte │ │ │ │ │ └── +page.ts │ │ │ │ ├── +page.ts │ │ │ │ ├── unsorted │ │ │ │ │ ├── +page.svelte │ │ │ │ │ └── +page.ts │ │ │ │ ├── group │ │ │ │ │ ├── [slug] │ │ │ │ │ │ └── +page.svelte │ │ │ │ │ ├── delete │ │ │ │ │ │ ├── schema.ts │ │ │ │ │ │ └── +page.server.ts │ │ │ │ │ └── new │ │ │ │ │ │ ├── schema.ts │ │ │ │ │ │ └── +page.server.ts │ │ │ │ ├── save │ │ │ │ │ ├── new │ │ │ │ │ │ ├── schema.ts │ │ │ │ │ │ └── +page.server.ts │ │ │ │ │ └── edit │ │ │ │ │ │ └── [id] │ │ │ │ │ │ └── schema.ts │ │ │ │ └── +page.svelte │ │ │ ├── help │ │ │ │ └── +page.ts │ │ │ ├── (auth) │ │ │ │ ├── login │ │ │ │ │ ├── +page.ts │ │ │ │ │ ├── github │ │ │ │ │ │ └── +server.ts │ │ │ │ │ ├── +page.svelte │ │ │ │ │ ├── google │ │ │ │ │ │ └── +server.ts │ │ │ │ │ └── user-login-form.svelte │ │ │ │ └── logout │ │ │ │ │ └── +page.server.ts │ │ │ ├── extension │ │ │ │ └── +page.ts │ │ │ ├── (legal) │ │ │ │ └── terms │ │ │ │ │ └── +page.svelte │ │ │ ├── api │ │ │ │ ├── explore │ │ │ │ │ ├── +server.ts │ │ │ │ │ └── explore.json │ │ │ │ ├── groups │ │ │ │ │ ├── +server.ts │ │ │ │ │ ├── new │ │ │ │ │ │ └── +server.ts │ │ │ │ │ └── sort │ │ │ │ │ │ └── +server.ts │ │ │ │ ├── saves │ │ │ │ │ ├── [id] │ │ │ │ │ │ └── +server.ts │ │ │ │ │ ├── delete │ │ │ │ │ │ └── [id] │ │ │ │ │ │ │ └── +server.ts │ │ │ │ │ ├── +server.ts │ │ │ │ │ └── metadata │ │ │ │ │ │ └── update │ │ │ │ │ │ └── +server.ts │ │ │ │ └── connect │ │ │ │ │ └── generate-key │ │ │ │ │ └── +server.ts │ │ │ ├── +layout.server.ts │ │ │ ├── +error.svelte │ │ │ └── +layout.svelte │ │ ├── app.html │ │ └── app.d.ts │ ├── postcss.config.cjs │ ├── static │ │ ├── og.png │ │ ├── poof.riv │ │ ├── vault.png │ │ ├── favicon.png │ │ └── screenshot.png │ ├── .eslintrc.cjs │ ├── .prettierignore │ ├── .env.example │ ├── .eslintignore │ ├── .gitignore │ ├── drizzle │ │ └── meta │ │ │ └── _journal.json │ ├── .prettierrc │ ├── tailwind.config.ts │ ├── svelte.config.js │ ├── drizzle.config.ts │ ├── tsconfig.json │ └── vite.config.ts ├── extension │ ├── .env │ ├── src │ │ ├── entries │ │ │ ├── background │ │ │ │ ├── script.ts │ │ │ │ └── serviceWorker.ts │ │ │ └── contentScript │ │ │ │ ├── primary │ │ │ │ └── main.ts │ │ │ │ ├── utils │ │ │ │ └── index.ts │ │ │ │ └── renderContent.ts │ │ ├── vite-env.d.ts │ │ └── app.pcss │ ├── postcss.config.cjs │ ├── .npmrc │ ├── .gitignore │ ├── public │ │ └── icons │ │ │ ├── icon16.png │ │ │ ├── icon32.png │ │ │ ├── icon48.png │ │ │ └── icon128.png │ ├── svelte.config.js │ ├── tailwind.config.ts │ ├── vite.config.ts │ └── tsconfig.json └── launcher │ ├── src-tauri │ ├── build.rs │ ├── icons │ │ ├── icon.ico │ │ ├── icon.png │ │ ├── 32x32.png │ │ ├── icon.icns │ │ ├── 128x128.png │ │ ├── StoreLogo.png │ │ ├── 128x128@2x.png │ │ ├── Square30x30Logo.png │ │ ├── Square44x44Logo.png │ │ ├── Square71x71Logo.png │ │ ├── Square89x89Logo.png │ │ ├── Square107x107Logo.png │ │ ├── Square142x142Logo.png │ │ ├── Square150x150Logo.png │ │ ├── Square284x284Logo.png │ │ ├── Square310x310Logo.png │ │ ├── ios │ │ │ ├── AppIcon-512@2x.png │ │ │ ├── AppIcon-20x20@1x.png │ │ │ ├── AppIcon-20x20@2x.png │ │ │ ├── AppIcon-20x20@3x.png │ │ │ ├── AppIcon-29x29@1x.png │ │ │ ├── AppIcon-29x29@2x.png │ │ │ ├── AppIcon-29x29@3x.png │ │ │ ├── AppIcon-40x40@1x.png │ │ │ ├── AppIcon-40x40@2x.png │ │ │ ├── AppIcon-40x40@3x.png │ │ │ ├── AppIcon-60x60@2x.png │ │ │ ├── AppIcon-60x60@3x.png │ │ │ ├── AppIcon-76x76@1x.png │ │ │ ├── AppIcon-76x76@2x.png │ │ │ ├── AppIcon-20x20@2x-1.png │ │ │ ├── AppIcon-29x29@2x-1.png │ │ │ ├── AppIcon-40x40@2x-1.png │ │ │ └── AppIcon-83.5x83.5@2x.png │ │ └── android │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_round.png │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_round.png │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_round.png │ │ │ └── ic_launcher_foreground.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_round.png │ │ │ └── ic_launcher_foreground.png │ │ │ └── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ ├── ic_launcher_round.png │ │ │ └── ic_launcher_foreground.png │ ├── .gitignore │ ├── Cargo.toml │ └── tauri.conf.json │ ├── .vscode │ ├── settings.json │ └── extensions.json │ ├── postcss.config.cjs │ ├── src │ ├── lib │ │ ├── command │ │ │ ├── index.ts │ │ │ ├── footers │ │ │ │ ├── index.ts │ │ │ │ └── new-stash-groups.svelte │ │ │ ├── pages │ │ │ │ └── index.ts │ │ │ ├── command-list-groups.svelte │ │ │ └── command-item-image.svelte │ │ ├── env.ts │ │ ├── utils.ts │ │ ├── stores.ts │ │ └── types.d.ts │ ├── routes │ │ ├── +page.svelte │ │ └── +layout.ts │ ├── app.html │ └── app.css │ ├── .env.example │ ├── app-icon.png │ ├── static │ ├── favicon.png │ └── vite.svg │ ├── .gitignore │ ├── tailwind.config.ts │ ├── svelte.config.js │ ├── tsconfig.json │ └── vite.config.js ├── .npmrc ├── pnpm-workspace.yaml ├── packages ├── ui │ ├── .eslintrc.cjs │ ├── components │ │ └── ui │ │ │ ├── tags │ │ │ └── index.ts │ │ │ ├── gradient │ │ │ ├── index.ts │ │ │ └── gradient.svelte │ │ │ ├── heading │ │ │ ├── index.ts │ │ │ └── heading.svelte │ │ │ ├── masonry │ │ │ └── index.ts │ │ │ ├── section │ │ │ ├── index.ts │ │ │ └── section.svelte │ │ │ ├── typography │ │ │ ├── index.ts │ │ │ └── typography.svelte │ │ │ ├── label │ │ │ ├── index.ts │ │ │ └── label.svelte │ │ │ ├── sonner │ │ │ ├── index.ts │ │ │ └── sonner.svelte │ │ │ ├── checkbox │ │ │ ├── index.ts │ │ │ └── checkbox.svelte │ │ │ ├── shortcut │ │ │ └── index.ts │ │ │ ├── slider │ │ │ ├── index.ts │ │ │ └── slider.svelte │ │ │ ├── switch │ │ │ ├── index.ts │ │ │ └── switch.svelte │ │ │ ├── skeleton │ │ │ ├── index.ts │ │ │ └── skeleton.svelte │ │ │ ├── separator │ │ │ ├── index.ts │ │ │ └── separator.svelte │ │ │ ├── dialog │ │ │ ├── dialog-portal.svelte │ │ │ ├── dialog-header.svelte │ │ │ ├── dialog-footer.svelte │ │ │ ├── dialog-title.svelte │ │ │ ├── dialog-description.svelte │ │ │ ├── dialog.svelte │ │ │ ├── dialog-overlay.svelte │ │ │ ├── index.ts │ │ │ └── dialog-content.svelte │ │ │ ├── scroll-area │ │ │ ├── index.ts │ │ │ ├── scroll-area-scrollbar.svelte │ │ │ └── scroll-area.svelte │ │ │ ├── alert-dialog │ │ │ ├── alert-dialog-portal.svelte │ │ │ ├── alert-dialog-header.svelte │ │ │ ├── alert-dialog-footer.svelte │ │ │ ├── alert-dialog-description.svelte │ │ │ ├── alert-dialog-title.svelte │ │ │ ├── alert-dialog.svelte │ │ │ ├── alert-dialog-action.svelte │ │ │ ├── alert-dialog-cancel.svelte │ │ │ ├── alert-dialog-overlay.svelte │ │ │ ├── alert-dialog-content.svelte │ │ │ └── index.ts │ │ │ ├── avatar │ │ │ ├── index.ts │ │ │ ├── avatar-fallback.svelte │ │ │ ├── avatar-image.svelte │ │ │ └── avatar.svelte │ │ │ ├── form │ │ │ ├── form-button.svelte │ │ │ ├── form-legend.svelte │ │ │ ├── form-description.svelte │ │ │ ├── form-label.svelte │ │ │ ├── form-field-errors.svelte │ │ │ ├── form-fieldset.svelte │ │ │ ├── form-field.svelte │ │ │ ├── index.ts │ │ │ └── form-element-field.svelte │ │ │ ├── command │ │ │ ├── command-footer.svelte │ │ │ ├── command-separator.svelte │ │ │ ├── command-empty.svelte │ │ │ ├── command-shortcut.svelte │ │ │ ├── command-loading.svelte │ │ │ ├── command-list.svelte │ │ │ ├── command-group.svelte │ │ │ ├── command-breadcrumbs.svelte │ │ │ ├── command-item.svelte │ │ │ ├── command.svelte │ │ │ ├── command-input.svelte │ │ │ ├── index.ts │ │ │ └── command-dialog.svelte │ │ │ ├── tooltip │ │ │ ├── index.ts │ │ │ └── tooltip-content.svelte │ │ │ ├── radio-group │ │ │ ├── index.ts │ │ │ ├── radio-group.svelte │ │ │ └── radio-group-item.svelte │ │ │ ├── context-menu │ │ │ ├── context-menu-radio-group.svelte │ │ │ ├── context-menu-separator.svelte │ │ │ ├── context-menu-shortcut.svelte │ │ │ ├── context-menu-label.svelte │ │ │ ├── context-menu-content.svelte │ │ │ ├── context-menu-sub-content.svelte │ │ │ ├── context-menu-item.svelte │ │ │ ├── context-menu-sub-trigger.svelte │ │ │ ├── context-menu-checkbox-item.svelte │ │ │ └── context-menu-radio-item.svelte │ │ │ ├── card │ │ │ ├── card-content.svelte │ │ │ ├── card-footer.svelte │ │ │ ├── card-header.svelte │ │ │ ├── card-description.svelte │ │ │ ├── card.svelte │ │ │ ├── card-title.svelte │ │ │ └── index.ts │ │ │ ├── dropdown-menu │ │ │ ├── dropdown-menu-radio-group.svelte │ │ │ ├── dropdown-menu-shortcut.svelte │ │ │ ├── dropdown-menu-separator.svelte │ │ │ ├── dropdown-menu-label.svelte │ │ │ ├── dropdown-menu-content.svelte │ │ │ ├── dropdown-menu-sub-content.svelte │ │ │ ├── dropdown-menu-item.svelte │ │ │ ├── dropdown-menu-sub-trigger.svelte │ │ │ ├── dropdown-menu-radio-item.svelte │ │ │ ├── dropdown-menu-checkbox-item.svelte │ │ │ └── dropdown-menu-link.svelte │ │ │ ├── select │ │ │ ├── select-separator.svelte │ │ │ ├── select-label.svelte │ │ │ ├── index.ts │ │ │ ├── select-trigger.svelte │ │ │ ├── select-content.svelte │ │ │ └── select-item.svelte │ │ │ ├── drawer │ │ │ ├── drawer-footer.svelte │ │ │ ├── drawer-nested.svelte │ │ │ ├── drawer-overlay.svelte │ │ │ ├── drawer-description.svelte │ │ │ ├── drawer-header.svelte │ │ │ ├── drawer-title.svelte │ │ │ ├── drawer.svelte │ │ │ └── index.ts │ │ │ ├── badge │ │ │ ├── badge.svelte │ │ │ └── index.ts │ │ │ ├── button │ │ │ └── button.svelte │ │ │ ├── input │ │ │ ├── index.ts │ │ │ └── input.svelte │ │ │ └── textarea │ │ │ ├── index.ts │ │ │ └── textarea.svelte │ ├── index.d.ts │ ├── components.json │ ├── icons │ │ ├── google.svelte │ │ └── stash.svelte │ ├── postcss.config.cjs │ └── tsconfig.json ├── constants │ ├── index.js │ ├── package.json │ └── siteconfig.js ├── tsconfig │ ├── package.json │ └── tsconfig-base.json └── config-eslint │ ├── package.json │ └── index.js ├── .prettierrc ├── .vscode └── settings.json ├── .eslintrc.cjs ├── nixpacks.toml ├── .prettierignore ├── turbo.json ├── .gitignore ├── README.md └── package.json /apps/web/turso/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /apps/web/.npmrc: -------------------------------------------------------------------------------- 1 | engine-strict=true -------------------------------------------------------------------------------- /.npmrc: -------------------------------------------------------------------------------- 1 | auto-install-peers = true 2 | -------------------------------------------------------------------------------- /apps/extension/.env: -------------------------------------------------------------------------------- 1 | MANIFEST_VERSION=3 -------------------------------------------------------------------------------- /apps/web/src/lib/types.d.ts: -------------------------------------------------------------------------------- 1 | export type TODO = any; -------------------------------------------------------------------------------- /apps/extension/src/entries/background/script.ts: -------------------------------------------------------------------------------- 1 | import "./main"; 2 | -------------------------------------------------------------------------------- /pnpm-workspace.yaml: -------------------------------------------------------------------------------- 1 | packages: 2 | - "apps/*" 3 | - "packages/*" 4 | -------------------------------------------------------------------------------- /apps/extension/src/entries/background/serviceWorker.ts: -------------------------------------------------------------------------------- 1 | import "./main"; 2 | -------------------------------------------------------------------------------- /apps/launcher/src-tauri/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | tauri_build::build() 3 | } 4 | -------------------------------------------------------------------------------- /apps/web/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = require('@repo/ui/postcss.config'); -------------------------------------------------------------------------------- /apps/extension/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = require('@repo/ui/postcss.config'); -------------------------------------------------------------------------------- /apps/launcher/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "svelte.enable-ts-plugin": true 3 | } 4 | -------------------------------------------------------------------------------- /apps/launcher/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | module.exports = require('@repo/ui/postcss.config'); -------------------------------------------------------------------------------- /apps/launcher/src/lib/command/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Command } from "./main.svelte"; -------------------------------------------------------------------------------- /apps/web/static/og.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/web/static/og.png -------------------------------------------------------------------------------- /apps/launcher/.env.example: -------------------------------------------------------------------------------- 1 | STRONGHOLD_SALT='my-random-string' 2 | STRONGHOLD_PASSWORD='vault-password' -------------------------------------------------------------------------------- /apps/web/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@repo/eslint-config/index.js'] 3 | }; 4 | -------------------------------------------------------------------------------- /apps/web/src/lib/components/app/groups/index.ts: -------------------------------------------------------------------------------- 1 | export { default as GroupsList } from './list.svelte'; -------------------------------------------------------------------------------- /apps/web/static/poof.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/web/static/poof.riv -------------------------------------------------------------------------------- /apps/web/static/vault.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/web/static/vault.png -------------------------------------------------------------------------------- /packages/ui/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | extends: ['@repo/eslint-config/index.js'] 3 | }; 4 | -------------------------------------------------------------------------------- /packages/ui/components/ui/tags/index.ts: -------------------------------------------------------------------------------- 1 | import Tags from './tags.svelte'; 2 | 3 | export default Tags; -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true, 3 | "trailingComma": "none", 4 | "printWidth": 100 5 | } 6 | -------------------------------------------------------------------------------- /apps/extension/.npmrc: -------------------------------------------------------------------------------- 1 | # auto-install-peers=false 2 | # strict-peer-dependencies=false 3 | engine-strict=true -------------------------------------------------------------------------------- /apps/launcher/app-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/app-icon.png -------------------------------------------------------------------------------- /apps/web/src/lib/components/site/hero/index.ts: -------------------------------------------------------------------------------- 1 | import Hero from "./hero.svelte"; 2 | 3 | export { Hero }; -------------------------------------------------------------------------------- /apps/web/src/lib/index.ts: -------------------------------------------------------------------------------- 1 | // place files you want to import through the `$lib` alias in this folder. 2 | -------------------------------------------------------------------------------- /apps/web/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/web/static/favicon.png -------------------------------------------------------------------------------- /packages/ui/index.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | 3 | declare module "@repo/ui/tailwind.config"; -------------------------------------------------------------------------------- /apps/extension/.gitignore: -------------------------------------------------------------------------------- 1 | **/dist 2 | **/node_modules 3 | .DS_Store 4 | dist.zip 5 | vite.config.ts.timestamp-* -------------------------------------------------------------------------------- /apps/web/static/screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/web/static/screenshot.png -------------------------------------------------------------------------------- /apps/launcher/static/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/static/favicon.png -------------------------------------------------------------------------------- /packages/ui/components/ui/gradient/index.ts: -------------------------------------------------------------------------------- 1 | import Gradient from './gradient.svelte'; 2 | 3 | export { Gradient }; -------------------------------------------------------------------------------- /packages/ui/components/ui/heading/index.ts: -------------------------------------------------------------------------------- 1 | import Heading from './heading.svelte'; 2 | 3 | export default Heading; -------------------------------------------------------------------------------- /packages/ui/components/ui/masonry/index.ts: -------------------------------------------------------------------------------- 1 | import Masonry from './masonry.svelte'; 2 | 3 | export default Masonry; -------------------------------------------------------------------------------- /packages/ui/components/ui/section/index.ts: -------------------------------------------------------------------------------- 1 | import Section from './section.svelte'; 2 | 3 | export default Section; -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "eslint.workingDirectories": [ 3 | { 4 | "mode": "auto" 5 | } 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /apps/web/.prettierignore: -------------------------------------------------------------------------------- 1 | # Ignore files for PNPM, NPM and YARN 2 | pnpm-lock.yaml 3 | package-lock.json 4 | yarn.lock 5 | -------------------------------------------------------------------------------- /apps/extension/public/icons/icon16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/extension/public/icons/icon16.png -------------------------------------------------------------------------------- /apps/extension/public/icons/icon32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/extension/public/icons/icon32.png -------------------------------------------------------------------------------- /apps/extension/public/icons/icon48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/extension/public/icons/icon48.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/icon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/icon.ico -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/icon.png -------------------------------------------------------------------------------- /apps/web/src/lib/components/app/connect/index.ts: -------------------------------------------------------------------------------- 1 | export { default as GenerateKeyDialog } from './generate-key-dialog.svelte'; -------------------------------------------------------------------------------- /packages/ui/components/ui/typography/index.ts: -------------------------------------------------------------------------------- 1 | import Typography from './typography.svelte'; 2 | 3 | export default Typography; -------------------------------------------------------------------------------- /apps/extension/public/icons/icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/extension/public/icons/icon128.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/32x32.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/icon.icns -------------------------------------------------------------------------------- /apps/web/src/lib/actions/index.ts: -------------------------------------------------------------------------------- 1 | import { gridArrowKeys } from './grid-arrow-keys'; 2 | 3 | export { 4 | gridArrowKeys 5 | } -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/128x128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/128x128.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/StoreLogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/StoreLogo.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/128x128@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/128x128@2x.png -------------------------------------------------------------------------------- /apps/web/src/lib/components/app/command/index.ts: -------------------------------------------------------------------------------- 1 | import CommandMenu from "./command-menu.svelte"; 2 | 3 | export { 4 | CommandMenu, 5 | } -------------------------------------------------------------------------------- /packages/ui/components/ui/label/index.ts: -------------------------------------------------------------------------------- 1 | import Root from "./label.svelte"; 2 | 3 | export { 4 | Root, 5 | // 6 | Root as Label, 7 | }; 8 | -------------------------------------------------------------------------------- /packages/ui/components/ui/sonner/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Toaster } from './sonner.svelte'; 2 | export { toast } from 'svelte-sonner'; 3 | -------------------------------------------------------------------------------- /apps/launcher/src/routes/+page.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /packages/ui/components/ui/checkbox/index.ts: -------------------------------------------------------------------------------- 1 | import Root from './checkbox.svelte'; 2 | export { 3 | Root, 4 | // 5 | Root as Checkbox 6 | }; 7 | -------------------------------------------------------------------------------- /packages/ui/components/ui/shortcut/index.ts: -------------------------------------------------------------------------------- 1 | import Root from "./shortcut.svelte"; 2 | 3 | export { 4 | Root, 5 | // 6 | Root as Shortcut, 7 | }; -------------------------------------------------------------------------------- /packages/ui/components/ui/slider/index.ts: -------------------------------------------------------------------------------- 1 | import Root from "./slider.svelte"; 2 | 3 | export { 4 | Root, 5 | // 6 | Root as Slider, 7 | }; 8 | -------------------------------------------------------------------------------- /packages/ui/components/ui/switch/index.ts: -------------------------------------------------------------------------------- 1 | import Root from './switch.svelte'; 2 | 3 | export { 4 | Root, 5 | // 6 | Root as Switch 7 | }; 8 | -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/Square30x30Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/Square30x30Logo.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/Square44x44Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/Square44x44Logo.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/Square71x71Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/Square71x71Logo.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/Square89x89Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/Square89x89Logo.png -------------------------------------------------------------------------------- /packages/ui/components/ui/skeleton/index.ts: -------------------------------------------------------------------------------- 1 | import Root from './skeleton.svelte'; 2 | 3 | export { 4 | Root, 5 | // 6 | Root as Skeleton 7 | }; 8 | -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/Square107x107Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/Square107x107Logo.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/Square142x142Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/Square142x142Logo.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/Square150x150Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/Square150x150Logo.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/Square284x284Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/Square284x284Logo.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/Square310x310Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/Square310x310Logo.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/ios/AppIcon-512@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/ios/AppIcon-512@2x.png -------------------------------------------------------------------------------- /apps/web/src/routes/homepage/+server.ts: -------------------------------------------------------------------------------- 1 | import { redirect } from '@sveltejs/kit'; 2 | 3 | export function GET() { 4 | redirect(302, '/?homepage'); 5 | } -------------------------------------------------------------------------------- /packages/ui/components/ui/separator/index.ts: -------------------------------------------------------------------------------- 1 | import Root from './separator.svelte'; 2 | 3 | export { 4 | Root, 5 | // 6 | Root as Separator 7 | }; 8 | -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/ios/AppIcon-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/ios/AppIcon-20x20@1x.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/ios/AppIcon-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/ios/AppIcon-20x20@2x.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/ios/AppIcon-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/ios/AppIcon-20x20@3x.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/ios/AppIcon-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/ios/AppIcon-29x29@1x.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/ios/AppIcon-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/ios/AppIcon-29x29@2x.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/ios/AppIcon-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/ios/AppIcon-29x29@3x.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/ios/AppIcon-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/ios/AppIcon-40x40@1x.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/ios/AppIcon-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/ios/AppIcon-40x40@2x.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/ios/AppIcon-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/ios/AppIcon-40x40@3x.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/ios/AppIcon-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/ios/AppIcon-60x60@2x.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/ios/AppIcon-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/ios/AppIcon-60x60@3x.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/ios/AppIcon-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/ios/AppIcon-76x76@1x.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/ios/AppIcon-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/ios/AppIcon-76x76@2x.png -------------------------------------------------------------------------------- /apps/web/src/lib/components/app/saves/item/index.ts: -------------------------------------------------------------------------------- 1 | export { default as Item } from './item.svelte'; 2 | export { default as ItemImage } from './item-image.svelte'; -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/ios/AppIcon-20x20@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/ios/AppIcon-20x20@2x-1.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/ios/AppIcon-29x29@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/ios/AppIcon-29x29@2x-1.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/ios/AppIcon-40x40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/ios/AppIcon-40x40@2x-1.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/ios/AppIcon-83.5x83.5@2x.png -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | // This tells ESLint to load the config from the package `eslint-config-custom` 4 | extends: ['custom'] 5 | }; 6 | -------------------------------------------------------------------------------- /apps/web/src/routes/main/groups/+page.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /apps/web/src/routes/main/+page.ts: -------------------------------------------------------------------------------- 1 | import type { PageLoad } from './$types'; 2 | 3 | export const load = (async () => { 4 | return { title: 'All' }; 5 | }) satisfies PageLoad; -------------------------------------------------------------------------------- /apps/extension/src/vite-env.d.ts: -------------------------------------------------------------------------------- 1 | /// 2 | /// 3 | /// 4 | -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/android/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/android/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /apps/web/src/routes/help/+page.ts: -------------------------------------------------------------------------------- 1 | import type { PageLoad } from './$types'; 2 | 3 | export const load = (async () => { 4 | return { title: 'Help' }; 5 | }) satisfies PageLoad; -------------------------------------------------------------------------------- /packages/constants/index.js: -------------------------------------------------------------------------------- 1 | import { siteConfig } from './siteconfig.js'; 2 | import { gradients } from './gradients.js'; 3 | 4 | export { 5 | gradients, 6 | siteConfig 7 | } -------------------------------------------------------------------------------- /apps/extension/src/app.pcss: -------------------------------------------------------------------------------- 1 | * { 2 | @apply border-border; 3 | } 4 | #stashlist-root { 5 | @apply bg-background text-foreground font-sans; 6 | font-size: 1rem !important; 7 | } 8 | 9 | -------------------------------------------------------------------------------- /apps/launcher/.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "svelte.svelte-vscode", 4 | "tauri-apps.tauri-vscode", 5 | "rust-lang.rust-analyzer" 6 | ] 7 | } 8 | -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/android/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /apps/web/src/routes/(auth)/login/+page.ts: -------------------------------------------------------------------------------- 1 | import type { PageLoad } from './$types'; 2 | 3 | export const load = (async () => { 4 | return { title: 'Login' }; 5 | }) satisfies PageLoad; -------------------------------------------------------------------------------- /apps/web/src/routes/main/groups/+page.ts: -------------------------------------------------------------------------------- 1 | import type { PageLoad } from './$types'; 2 | 3 | export const load = (async () => { 4 | return { title: 'Groups' }; 5 | }) satisfies PageLoad; -------------------------------------------------------------------------------- /apps/web/src/routes/main/unsorted/+page.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /nixpacks.toml: -------------------------------------------------------------------------------- 1 | providers = ["node"] 2 | 3 | [phases.install] 4 | cmds = ["npm install -g corepack", "corepack enable", "corepack prepare pnpm@9.2.0 --activate", "pnpm install --filter web"] -------------------------------------------------------------------------------- /apps/launcher/src/lib/env.ts: -------------------------------------------------------------------------------- 1 | import { invoke } from "@tauri-apps/api/core"; 2 | 3 | export async function getEnv(name: string) { 4 | return await invoke("get_env", { name }); 5 | } -------------------------------------------------------------------------------- /apps/web/src/routes/extension/+page.ts: -------------------------------------------------------------------------------- 1 | import type { PageLoad } from './$types'; 2 | 3 | export const load = (async () => { 4 | return { title: 'Extension' }; 5 | }) satisfies PageLoad; -------------------------------------------------------------------------------- /apps/web/src/routes/main/unsorted/+page.ts: -------------------------------------------------------------------------------- 1 | import type { PageLoad } from './$types'; 2 | 3 | export const load = (async () => { 4 | return { title: 'Unsorted' }; 5 | }) satisfies PageLoad; -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/android/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/android/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /apps/web/src/routes/main/group/[slug]/+page.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/android/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/android/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/android/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/android/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/jramke/stashlist/HEAD/apps/launcher/src-tauri/icons/android/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /apps/launcher/src-tauri/.gitignore: -------------------------------------------------------------------------------- 1 | # Generated by Cargo 2 | # will have compiled files and executables 3 | /target/ 4 | 5 | # Generated by Tauri 6 | # will have schema files for capabilities auto-completion 7 | /gen/schemas 8 | -------------------------------------------------------------------------------- /apps/web/src/routes/main/group/delete/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | export const formSchema = z.object({ 3 | id: z.string().min(1), 4 | isOnCurrentSlug: z.boolean() 5 | }); 6 | export type FormSchema = typeof formSchema; 7 | -------------------------------------------------------------------------------- /apps/web/src/routes/main/save/new/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | export const formSchema = z.object({ 3 | input: z.string().min(1), 4 | groups: z.string().optional() 5 | }); 6 | export type FormSchema = typeof formSchema; 7 | -------------------------------------------------------------------------------- /packages/constants/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@repo/constants", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "main": "index.js", 6 | "type": "module", 7 | "exports": { 8 | ".": "./index.js" 9 | } 10 | } -------------------------------------------------------------------------------- /apps/web/.env.example: -------------------------------------------------------------------------------- 1 | DEV='1' 2 | 3 | DATABASE_AUTH_TOKEN='' 4 | # DATABASE_URL='' 5 | DATABASE_URL='http://127.0.0.1:8080' 6 | 7 | GITHUB_CLIENT_ID='' 8 | GITHUB_CLIENT_SECRET='' 9 | 10 | GOOGLE_CLIENT_ID='' 11 | GOOGLE_CLIENT_SECRET='' -------------------------------------------------------------------------------- /apps/web/.eslintignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | 10 | # Ignore files for PNPM, NPM and YARN 11 | pnpm-lock.yaml 12 | package-lock.json 13 | yarn.lock 14 | -------------------------------------------------------------------------------- /apps/web/src/routes/main/group/new/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | export const formSchema = z.object({ 3 | title: z.string().min(2), 4 | gradientIndex: z.number().optional(), 5 | }); 6 | export type FormSchema = typeof formSchema; 7 | -------------------------------------------------------------------------------- /apps/launcher/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | vite.config.js.timestamp-* 10 | vite.config.ts.timestamp-* 11 | static/groups.json 12 | static/saves.json 13 | -------------------------------------------------------------------------------- /apps/web/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /build 4 | /.svelte-kit 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | vite.config.js.timestamp-* 10 | vite.config.ts.timestamp-* 11 | turso/* 12 | !turso/.gitkeep 13 | .vercel 14 | stashlist-replica* -------------------------------------------------------------------------------- /apps/web/src/lib/components/app/index.ts: -------------------------------------------------------------------------------- 1 | import EmptyState from './empty-state.svelte'; 2 | import Gradient from './gradient.svelte'; 3 | import CopyButton from './copy-button.svelte'; 4 | 5 | export { 6 | EmptyState, 7 | Gradient, 8 | CopyButton 9 | } -------------------------------------------------------------------------------- /apps/extension/src/entries/contentScript/primary/main.ts: -------------------------------------------------------------------------------- 1 | import renderContent from "../renderContent"; 2 | import App from "./App.svelte"; 3 | 4 | renderContent(import.meta.PLUGIN_WEB_EXT_CHUNK_CSS_PATHS, (appRoot) => { 5 | new App({ 6 | target: appRoot, 7 | }); 8 | }); 9 | -------------------------------------------------------------------------------- /.prettierignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .svelte-kit 3 | node_modules 4 | /build 5 | /package 6 | .env 7 | .env.* 8 | !.env.example 9 | vite.config.js* 10 | 11 | # Ignore files for PNPM, NPM and YARN 12 | pnpm-lock.yaml 13 | pnpm-workspace.yaml 14 | package-lock.json 15 | yarn.lock 16 | -------------------------------------------------------------------------------- /packages/ui/components/ui/dialog/dialog-portal.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /apps/web/src/lib/components/app/saves/index.ts: -------------------------------------------------------------------------------- 1 | // import List from './list.svelte'; 2 | // import { Item } from './item'; 3 | 4 | // export { 5 | // List as SavesList, 6 | // Item 7 | // } 8 | 9 | export { default as SavesList } from './list.svelte'; 10 | export * from './item'; -------------------------------------------------------------------------------- /apps/launcher/src/routes/+layout.ts: -------------------------------------------------------------------------------- 1 | // Tauri doesn't have a Node.js server to do proper SSR 2 | // so we will use adapter-static to prerender the app (SSG) 3 | // See: https://beta.tauri.app/start/frontend/sveltekit/ for more info 4 | export const prerender = true; 5 | export const ssr = false; 6 | -------------------------------------------------------------------------------- /apps/web/drizzle/meta/_journal.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "6", 3 | "dialect": "sqlite", 4 | "entries": [ 5 | { 6 | "idx": 0, 7 | "version": "6", 8 | "when": 1720889812656, 9 | "tag": "0000_spooky_marvel_zombies", 10 | "breakpoints": true 11 | } 12 | ] 13 | } -------------------------------------------------------------------------------- /apps/web/src/lib/components/site/index.ts: -------------------------------------------------------------------------------- 1 | import Footer from './footer.svelte'; 2 | import Navigation from './navigation.svelte'; 3 | import Metadata from './metadata.svelte'; 4 | import { Hero } from './hero'; 5 | 6 | export { 7 | Footer, 8 | Navigation, 9 | Metadata, 10 | Hero, 11 | } -------------------------------------------------------------------------------- /apps/web/src/routes/(legal)/terms/+page.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | Terms 9 | 10 | 11 | -------------------------------------------------------------------------------- /packages/tsconfig/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@repo/tsconfig", 3 | "version": "0.0.0", 4 | "license": "MIT", 5 | "files": [ 6 | "tsconfig-base.json", 7 | "tsconfig-svelte-library.json" 8 | ], 9 | "devDependencies": { 10 | "@tsconfig/svelte": "^5.0.4" 11 | } 12 | } -------------------------------------------------------------------------------- /apps/web/src/lib/components/app/nav/index.ts: -------------------------------------------------------------------------------- 1 | export {default as Link } from "./link.svelte"; 2 | export {default as Topbar } from "./topbar.svelte"; 3 | export {default as Userinfo } from "./userinfo.svelte"; 4 | export {default as Groups } from "./groups.svelte"; 5 | export {default as NewStashInput } from "./new-stash-input.svelte"; -------------------------------------------------------------------------------- /packages/ui/components/ui/scroll-area/index.ts: -------------------------------------------------------------------------------- 1 | import Scrollbar from "./scroll-area-scrollbar.svelte"; 2 | // import Root from "./scroll-area.svelte"; 3 | import Root from "./scroll-area-legacy.svelte"; 4 | 5 | export { 6 | Root, 7 | Scrollbar, 8 | //, 9 | Root as ScrollArea, 10 | Scrollbar as ScrollAreaScrollbar, 11 | }; 12 | -------------------------------------------------------------------------------- /packages/ui/components.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://shadcn-svelte.com/schema.json", 3 | "style": "default", 4 | "tailwind": { 5 | "config": "tailwind.config.ts", 6 | "css": "globals.pcss", 7 | "baseColor": "stone" 8 | }, 9 | "aliases": { 10 | "components": "$ui/components", 11 | "utils": "$ui/utils" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /packages/ui/components/ui/alert-dialog/alert-dialog-portal.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /packages/ui/components/ui/avatar/index.ts: -------------------------------------------------------------------------------- 1 | import Root from "./avatar.svelte"; 2 | import Image from "./avatar-image.svelte"; 3 | import Fallback from "./avatar-fallback.svelte"; 4 | 5 | export { 6 | Root, 7 | Image, 8 | Fallback, 9 | // 10 | Root as Avatar, 11 | Image as AvatarImage, 12 | Fallback as AvatarFallback, 13 | }; 14 | -------------------------------------------------------------------------------- /packages/ui/components/ui/form/form-button.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /packages/ui/components/ui/typography/typography.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /apps/web/.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "useTabs": true, 3 | "singleQuote": true, 4 | "trailingComma": "none", 5 | "printWidth": 100, 6 | "plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"], 7 | "overrides": [ 8 | { 9 | "files": "*.svelte", 10 | "options": { 11 | "parser": "svelte" 12 | } 13 | } 14 | ] 15 | } 16 | -------------------------------------------------------------------------------- /apps/extension/svelte.config.js: -------------------------------------------------------------------------------- 1 | import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; 2 | import sveltePreprocess from "svelte-preprocess"; 3 | 4 | export default { 5 | // Consult https://github.com/sveltejs/svelte-preprocess 6 | // for more information about preprocessors 7 | preprocess: [sveltePreprocess(), vitePreprocess({})], 8 | }; 9 | -------------------------------------------------------------------------------- /apps/launcher/src/lib/utils.ts: -------------------------------------------------------------------------------- 1 | export function memoize(func: (...args: any[]) => any) { 2 | const cache = new Map(); 3 | return function (...args: any[]) { 4 | const key = JSON.stringify(args); 5 | if (!cache.has(key)) { 6 | cache.set(key, func(...args)); 7 | } 8 | return cache.get(key); 9 | }; 10 | } -------------------------------------------------------------------------------- /apps/launcher/src/lib/command/footers/index.ts: -------------------------------------------------------------------------------- 1 | import type { ComponentType } from "svelte"; 2 | 3 | import NewStashGroups from "./new-stash-groups.svelte"; 4 | 5 | type FooterComponents = { 6 | [key: string]: ComponentType; 7 | } 8 | 9 | export const footerComponents = { 10 | newStashGroups: NewStashGroups 11 | } as const satisfies FooterComponents; -------------------------------------------------------------------------------- /apps/web/src/lib/components/app/saves/item/item-description.svelte: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /apps/web/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | import sharedConfig from "@repo/ui/tailwind.config"; 3 | 4 | const config: Pick = { 5 | content: [ 6 | './src/**/*.{html,js,svelte,ts}', 7 | '../../packages/ui/**/*.{html,js,svelte,ts}', 8 | ], 9 | presets: [sharedConfig], 10 | }; 11 | 12 | export default config; -------------------------------------------------------------------------------- /apps/extension/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | import sharedConfig from "@repo/ui/tailwind.config"; 3 | 4 | const config: Pick = { 5 | content: [ 6 | './src/**/*.{html,js,svelte,ts}', 7 | '../../packages/ui/**/*.{html,js,svelte,ts}', 8 | ], 9 | presets: [sharedConfig], 10 | }; 11 | 12 | export default config; -------------------------------------------------------------------------------- /apps/launcher/tailwind.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from "tailwindcss"; 2 | import sharedConfig from "@repo/ui/tailwind.config"; 3 | 4 | const config: Pick = { 5 | content: [ 6 | './src/**/*.{html,js,svelte,ts}', 7 | '../../packages/ui/**/*.{html,js,svelte,ts}', 8 | ], 9 | presets: [sharedConfig], 10 | }; 11 | 12 | export default config; -------------------------------------------------------------------------------- /packages/ui/components/ui/command/command-footer.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /packages/ui/components/ui/section/section.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /packages/ui/components/ui/tooltip/index.ts: -------------------------------------------------------------------------------- 1 | import { Tooltip as TooltipPrimitive } from "bits-ui"; 2 | import Content from "./tooltip-content.svelte"; 3 | 4 | const Root = TooltipPrimitive.Root; 5 | const Trigger = TooltipPrimitive.Trigger; 6 | 7 | export { 8 | Root, 9 | Trigger, 10 | Content, 11 | // 12 | Root as Tooltip, 13 | Content as TooltipContent, 14 | Trigger as TooltipTrigger, 15 | }; 16 | -------------------------------------------------------------------------------- /packages/ui/components/ui/radio-group/index.ts: -------------------------------------------------------------------------------- 1 | import { RadioGroup as RadioGroupPrimitive } from 'bits-ui'; 2 | 3 | import Root from './radio-group.svelte'; 4 | import Item from './radio-group-item.svelte'; 5 | const Input = RadioGroupPrimitive.Input; 6 | 7 | export { 8 | Root, 9 | Input, 10 | Item, 11 | // 12 | Root as RadioGroup, 13 | Input as RadioGroupInput, 14 | Item as RadioGroupItem 15 | }; 16 | -------------------------------------------------------------------------------- /packages/ui/components/ui/context-menu/context-menu-radio-group.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /packages/ui/components/ui/card/card-content.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/ui/components/ui/skeleton/skeleton.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /packages/ui/components/ui/dropdown-menu/dropdown-menu-radio-group.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /apps/web/src/routes/main/save/edit/[id]/schema.ts: -------------------------------------------------------------------------------- 1 | import { z } from 'zod'; 2 | export const formSchema = z.object({ 3 | id: z.string(), 4 | title: z.string(), 5 | description: z.string(), 6 | text: z.string(), 7 | groups: z.string(), 8 | // .transform( value => value.split( ',' ).map( String ) ) 9 | // .pipe( z.string().array() ), 10 | // imageUrl: z.string(), 11 | }); 12 | export type FormSchema = typeof formSchema; 13 | -------------------------------------------------------------------------------- /packages/config-eslint/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@repo/eslint-config", 3 | "version": "0.0.0", 4 | "dependencies": { 5 | "@typescript-eslint/eslint-plugin": "^7.6.0", 6 | "@typescript-eslint/parser": "^7.6.0", 7 | "eslint-config-prettier": "^9.1.0", 8 | "eslint-config-turbo": "^1.13.2", 9 | "eslint-plugin-svelte": "^2.37.0" 10 | }, 11 | "publishConfig": { 12 | "access": "public" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /packages/ui/components/ui/card/card-footer.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/ui/components/ui/card/card-header.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/ui/components/ui/select/select-separator.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /apps/web/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | %sveltekit.head% 8 | 9 | 10 | %sveltekit.body% 11 | 12 | 13 | -------------------------------------------------------------------------------- /apps/web/src/lib/components/site/logos/twitter-bookmark.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 16 | -------------------------------------------------------------------------------- /packages/ui/components/ui/command/command-separator.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /turbo.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://turbo.build/schema.json", 3 | "pipeline": { 4 | "build": { 5 | "dependsOn": ["^build"], 6 | "outputs": [".svelte-kit/**", ".build/**"] 7 | }, 8 | "preview": { 9 | "dependsOn": ["^build"], 10 | "outputs": [".svelte-kit/**", ".build/**"] 11 | }, 12 | "lint": {}, 13 | "dev": { 14 | "cache": false, 15 | "persistent": true 16 | } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /packages/ui/components/ui/card/card-description.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /apps/web/svelte.config.js: -------------------------------------------------------------------------------- 1 | import adapter from '@sveltejs/adapter-node'; 2 | import { vitePreprocess } from '@sveltejs/vite-plugin-svelte'; 3 | 4 | /** @type {import('@sveltejs/kit').Config} */ 5 | const config = { 6 | preprocess: [vitePreprocess({})], 7 | 8 | kit: { 9 | adapter: adapter(), 10 | csrf: { 11 | checkOrigin: false, 12 | }, 13 | alias: { 14 | '$routes': './src/routes', 15 | } 16 | } 17 | }; 18 | 19 | export default config; 20 | -------------------------------------------------------------------------------- /packages/ui/components/ui/dialog/dialog-header.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/ui/components/ui/alert-dialog/alert-dialog-header.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/ui/components/ui/dropdown-menu/dropdown-menu-shortcut.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/ui/components/ui/card/card.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /packages/ui/components/ui/command/command-empty.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/ui/components/ui/context-menu/context-menu-separator.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | -------------------------------------------------------------------------------- /packages/ui/components/ui/dropdown-menu/dropdown-menu-separator.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | -------------------------------------------------------------------------------- /packages/ui/components/ui/gradient/gradient.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 17 | -------------------------------------------------------------------------------- /apps/launcher/src/lib/stores.ts: -------------------------------------------------------------------------------- 1 | import { writable } from "svelte/store"; 2 | import type { CommandPage } from "./types"; 3 | 4 | export const apiKey = writable(''); 5 | export const currentPage = writable(undefined); 6 | 7 | export const saves = writable([]); 8 | export const groups = writable([]); 9 | export const loading = writable(false); 10 | 11 | export const newStashInputValue = writable(''); 12 | export const newStashSelectedGroups = writable([]); -------------------------------------------------------------------------------- /apps/web/src/lib/components/app/gradient.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 17 | -------------------------------------------------------------------------------- /packages/ui/components/ui/dialog/dialog-footer.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /apps/web/src/lib/components/app/saves/item/item-media.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | {#if $listLayout !== 'list'} 11 | 12 | 13 | 14 | {/if} -------------------------------------------------------------------------------- /packages/ui/components/ui/select/select-label.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /packages/ui/components/ui/alert-dialog/alert-dialog-footer.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /packages/ui/components/ui/command/command-shortcut.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /apps/launcher/src/lib/types.d.ts: -------------------------------------------------------------------------------- 1 | import type { ComponentType } from "svelte"; 2 | import type { Icon } from "lucide-svelte"; 3 | import { footerComponents } from "./command/footers"; 4 | 5 | 6 | export type CommandPage = { 7 | name: string; 8 | id: string; 9 | groupId?: string; 10 | placeholder?: string; 11 | preventFilter?: boolean; 12 | height?: number; 13 | icon?: ComponentType; 14 | hideFooter?: boolean; 15 | footer?: keyof typeof footerComponents; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /packages/ui/components/ui/context-menu/context-menu-shortcut.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /packages/ui/components/ui/dialog/dialog-title.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /packages/ui/components/ui/dialog/dialog-description.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /apps/launcher/src/app.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Tauri + SvelteKit + Typescript App 8 | %sveltekit.head% 9 | 10 | 11 | %sveltekit.body% 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/ui/components/ui/dialog/dialog.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /apps/web/src/routes/api/explore/+server.ts: -------------------------------------------------------------------------------- 1 | import type { RequestHandler } from './$types'; 2 | import explore from './explore.json'; 3 | import { error, json, redirect } from '@sveltejs/kit'; 4 | 5 | export const GET: RequestHandler = async ({ locals }) => { 6 | if (!locals.user) redirect(302, '/login'); 7 | 8 | try { 9 | return json(explore); 10 | 11 | } catch (err) { 12 | 13 | console.log('error fetch groups', err); 14 | return error(400, { 15 | message: 'Failed to fetch groups' 16 | }); 17 | } 18 | }; 19 | -------------------------------------------------------------------------------- /apps/web/src/lib/components/app/nav/link.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /apps/web/src/lib/server/db/index.ts: -------------------------------------------------------------------------------- 1 | import { drizzle } from 'drizzle-orm/libsql'; 2 | import { createClient } from '@libsql/client'; 3 | import { env } from '$env/dynamic/private'; 4 | import * as schema from './schema'; 5 | 6 | const client = createClient({ 7 | url: "file:stashlist-replica.db", 8 | syncUrl: env.DATABASE_URL, 9 | authToken: env.DATABASE_AUTH_TOKEN, 10 | encryptionKey: env.DATABASE_REPLICA_ENCRYPTION_KEY, 11 | syncInterval: 60, 12 | }); 13 | 14 | export const db = drizzle(client, { schema }); 15 | -------------------------------------------------------------------------------- /packages/ui/components/ui/heading/heading.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /packages/ui/components/ui/radio-group/radio-group.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /apps/launcher/src/lib/command/pages/index.ts: -------------------------------------------------------------------------------- 1 | import type { ComponentType } from "svelte"; 2 | import type { CommandPage } from "$lib/types"; 3 | 4 | import Connect from "./connect.svelte"; 5 | import NewStash from "./new-stash.svelte"; 6 | import NewStashGroups from "./new-stash-groups.svelte"; 7 | 8 | type PageComponents = { 9 | [key: CommandPage['id']]: ComponentType; 10 | } 11 | export const pageComponents: PageComponents = { 12 | 'connect': Connect, 13 | 'new-stash': NewStash, 14 | 'new-stash-groups': NewStashGroups 15 | }; -------------------------------------------------------------------------------- /packages/ui/components/ui/avatar/avatar-fallback.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /apps/launcher/svelte.config.js: -------------------------------------------------------------------------------- 1 | // Tauri doesn't have a Node.js server to do proper SSR 2 | // so we will use adapter-static to prerender the app (SSG) 3 | // See: https://beta.tauri.app/start/frontend/sveltekit/ for more info 4 | import adapter from "@sveltejs/adapter-static"; 5 | import { vitePreprocess } from "@sveltejs/vite-plugin-svelte"; 6 | 7 | /** @type {import('@sveltejs/kit').Config} */ 8 | const config = { 9 | preprocess: vitePreprocess(), 10 | kit: { 11 | adapter: adapter(), 12 | }, 13 | }; 14 | 15 | export default config; 16 | -------------------------------------------------------------------------------- /apps/web/src/lib/components/app/empty-state.svelte: -------------------------------------------------------------------------------- 1 | 8 | 9 | 10 | {title} 11 | {message} 12 | 13 | -------------------------------------------------------------------------------- /packages/ui/components/ui/drawer/drawer-footer.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /packages/ui/components/ui/form/form-legend.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /packages/ui/components/ui/alert-dialog/alert-dialog-description.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /packages/ui/components/ui/drawer/drawer-nested.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/ui/components/ui/alert-dialog/alert-dialog-title.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /packages/ui/components/ui/alert-dialog/alert-dialog.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /packages/ui/components/ui/badge/badge.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/ui/components/ui/drawer/drawer-overlay.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/ui/components/ui/avatar/avatar-image.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /apps/launcher/src/app.css: -------------------------------------------------------------------------------- 1 | * { 2 | @apply border-border; 3 | } 4 | #stashlist { 5 | @apply text-foreground; 6 | min-height: 100%; 7 | } 8 | body, main { 9 | height: 100vh; 10 | background-color: transparent; 11 | } 12 | main { 13 | @apply bg-background rounded-[10px] overflow-hidden; 14 | } 15 | [data-cmdk-list-sizer], .scroll-area { 16 | height: 100%; 17 | } 18 | 19 | /* [data-sonner-toaster] { 20 | --mobile-offset: 0px !important; 21 | --offset: 12px; 22 | } 23 | [data-sonner-toast] { 24 | width: fit-content !important; 25 | transform: translateX(50%); 26 | } */ -------------------------------------------------------------------------------- /packages/ui/components/ui/drawer/drawer-description.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/ui/components/ui/drawer/drawer-header.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /packages/ui/components/ui/drawer/drawer-title.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /apps/web/src/routes/api/groups/+server.ts: -------------------------------------------------------------------------------- 1 | import { getAllGroups } from '$lib/server/db/queries'; 2 | import type { RequestHandler } from './$types'; 3 | import { error, json, redirect } from '@sveltejs/kit'; 4 | 5 | export const GET: RequestHandler = async ({ locals }) => { 6 | if (!locals.user) redirect(302, '/login'); 7 | 8 | try { 9 | const groups = await getAllGroups(locals.user.id); 10 | return json(groups); 11 | } catch (err) { 12 | console.log('error fetch groups', err); 13 | return error(400, { 14 | message: 'Failed to fetch groups' 15 | }); 16 | } 17 | }; 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | node_modules 5 | .pnp 6 | .pnp.js 7 | 8 | # testing 9 | coverage 10 | 11 | # svelte 12 | .svelte-kit 13 | 14 | # misc 15 | .DS_Store 16 | *.pem 17 | 18 | # debug 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | 23 | # local env files 24 | .env.local 25 | .env.development.local 26 | .env.test.local 27 | .env.production.local 28 | 29 | # turbo 30 | .turbo 31 | 32 | # excalidraw 33 | *.excalidraw 34 | 35 | # notes 36 | NOTES.md 37 | bookmarks.html -------------------------------------------------------------------------------- /apps/web/src/routes/main/+page.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /packages/ui/components/ui/drawer/drawer.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/ui/components/ui/avatar/avatar.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/ui/components/ui/form/form-description.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /apps/web/src/lib/components/site/logos/github-star.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Stashlist — Your Creative Vault 2 | 3 | Save inspirational websites, images, code snippets, and more at one place. 4 | 5 | Stashlist comes with a web extension to easily save new stashes. 6 | 7 | Check out the website: [stashlist.app](https://www.stashlist.app/) 8 | 9 | ## Techstack 10 | - Svelte(kit) as framework 11 | - Drizzle ORM as orm 12 | - Turso as database 13 | - Lucia for authentication 14 | - Shadcn-svelte as base for ui 15 | - Turborepo for monorepo 16 | 17 | 18 | # TODOS 19 | 20 | - [ ] Caching 21 | - [ ] Types 22 | -------------------------------------------------------------------------------- /packages/ui/components/ui/label/label.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /packages/ui/icons/google.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | -------------------------------------------------------------------------------- /apps/web/src/lib/components/site/logos/codepen.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 20 | -------------------------------------------------------------------------------- /packages/ui/components/ui/form/form-label.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /apps/web/src/routes/(auth)/logout/+page.server.ts: -------------------------------------------------------------------------------- 1 | import { lucia } from '$lib/server/auth'; 2 | import { fail, redirect } from '@sveltejs/kit'; 3 | 4 | import type { Actions } from './$types'; 5 | 6 | export const actions: Actions = { 7 | default: async ({ locals, cookies }) => { 8 | if (!locals.session) { 9 | return fail(401); 10 | } 11 | await lucia.invalidateSession(locals.session.id); 12 | const sessionCookie = lucia.createBlankSessionCookie(); 13 | cookies.set(sessionCookie.name, sessionCookie.value, { 14 | path: '.', 15 | ...sessionCookie.attributes 16 | }); 17 | redirect(302, '/login'); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /apps/web/drizzle.config.ts: -------------------------------------------------------------------------------- 1 | import type { Config } from 'drizzle-kit'; 2 | import * as dotenv from 'dotenv'; 3 | dotenv.config(); 4 | 5 | const { DATABASE_URL, DATABASE_AUTH_TOKEN } = process.env; 6 | if (!DATABASE_URL) { 7 | throw new Error('No url'); 8 | } 9 | if (!DATABASE_AUTH_TOKEN) { 10 | throw new Error('No auth token'); 11 | } 12 | 13 | export default { 14 | schema: './src/lib/server/db/schema.ts', 15 | out: './drizzle', 16 | driver: 'turso', 17 | dialect: 'sqlite', 18 | dbCredentials: { 19 | url: DATABASE_URL, 20 | authToken: DATABASE_AUTH_TOKEN 21 | }, 22 | // strict: true 23 | verbose: true, 24 | } satisfies Config; 25 | -------------------------------------------------------------------------------- /apps/web/src/routes/api/saves/[id]/+server.ts: -------------------------------------------------------------------------------- 1 | import type { RequestHandler } from './$types'; 2 | 3 | import { error, json, redirect } from '@sveltejs/kit'; 4 | import { getSaveWithGroups } from '$lib/server/db/queries'; 5 | 6 | export const GET: RequestHandler = async ({ locals, params }) => { 7 | if (!locals.user) redirect(302, '/login'); 8 | 9 | try { 10 | const saveId = params.id; 11 | const saveWithGroups = await getSaveWithGroups(saveId); 12 | return json(saveWithGroups); 13 | } catch (err) { 14 | console.log('error fetch save', err); 15 | return error(400, { 16 | message: 'Failed to fetch save' 17 | }); 18 | } 19 | }; 20 | -------------------------------------------------------------------------------- /packages/ui/components/ui/card/card-title.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /packages/ui/components/ui/command/command-loading.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | *]:flex [&_>*]:items-center [&_>*]:justify-center [&_>*]:gap-2", className)} {...$$restProps}> 12 | 13 | 14 | -------------------------------------------------------------------------------- /packages/ui/components/ui/dropdown-menu/dropdown-menu-label.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /packages/ui/components/ui/context-menu/context-menu-label.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /packages/ui/components/ui/command/command-list.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /apps/web/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | // "checkJs": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true, 12 | "moduleResolution": "bundler" 13 | } 14 | // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias 15 | // 16 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes 17 | // from the referenced tsconfig.json - TypeScript does not merge them in 18 | } 19 | -------------------------------------------------------------------------------- /apps/launcher/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./.svelte-kit/tsconfig.json", 3 | "compilerOptions": { 4 | "allowJs": true, 5 | // "checkJs": true, 6 | "esModuleInterop": true, 7 | "forceConsistentCasingInFileNames": true, 8 | "resolveJsonModule": true, 9 | "skipLibCheck": true, 10 | "sourceMap": true, 11 | "strict": true, 12 | "moduleResolution": "bundler" 13 | } 14 | // Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias 15 | // 16 | // If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes 17 | // from the referenced tsconfig.json - TypeScript does not merge them in 18 | } 19 | -------------------------------------------------------------------------------- /apps/web/src/routes/(auth)/login/github/+server.ts: -------------------------------------------------------------------------------- 1 | import type { RequestEvent } from "@sveltejs/kit"; 2 | 3 | import { github } from "$lib/server/auth"; 4 | import { generateState } from "arctic"; 5 | import { redirect } from "@sveltejs/kit"; 6 | 7 | export async function GET(event: RequestEvent): Promise { 8 | const state = generateState(); 9 | const url = await github.createAuthorizationURL(state); 10 | 11 | event.cookies.set("github_oauth_state", state, { 12 | path: "/", 13 | secure: import.meta.env.PROD, 14 | httpOnly: true, 15 | maxAge: 60 * 10, // 10 min 16 | sameSite: "lax" 17 | }); 18 | 19 | return redirect(302, url.toString()); 20 | } -------------------------------------------------------------------------------- /apps/web/src/routes/api/saves/delete/[id]/+server.ts: -------------------------------------------------------------------------------- 1 | import type { RequestHandler } from './$types'; 2 | 3 | import { error, json, redirect } from '@sveltejs/kit'; 4 | import { deleteSave } from '$lib/server/db/queries'; 5 | 6 | export const POST: RequestHandler = async ({ locals, params }) => { 7 | if (!locals.user) redirect(302, '/login'); 8 | 9 | try { 10 | const saveId = params.id; 11 | console.log('delete save', saveId); 12 | await deleteSave(saveId); 13 | return json({ success: true }); 14 | } catch (err) { 15 | console.log('error delete save', err); 16 | return error(400, { 17 | message: 'Failed to delete save' 18 | }); 19 | } 20 | }; 21 | 22 | -------------------------------------------------------------------------------- /apps/web/src/routes/+layout.server.ts: -------------------------------------------------------------------------------- 1 | import { siteConfig } from '@repo/constants'; 2 | import { getLatestReleaseByOS, getLatestReleases, getOS } from '$lib/helper/releases'; 3 | import type { LayoutServerLoad } from './$types'; 4 | import { redirect } from '@sveltejs/kit'; 5 | 6 | export const load: LayoutServerLoad = async ({ locals, route, request }) => { 7 | if (!locals.user && route.id?.includes(siteConfig.appUrl)) redirect(302, '/login'); 8 | 9 | return { 10 | user: locals.user, 11 | os: getOS(request.headers.get('user-agent') ?? null), 12 | // releaseInfo: getLatestReleaseByOS('jramke/stashlist', request.headers.get('user-agent') ?? null), 13 | }; 14 | }; 15 | -------------------------------------------------------------------------------- /apps/web/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { sveltekit } from '@sveltejs/kit/vite'; 2 | import { defineConfig } from 'vite'; 3 | import tsconfigPaths from 'vite-tsconfig-paths'; 4 | 5 | export default defineConfig({ 6 | plugins: [ 7 | sveltekit(), 8 | tsconfigPaths({ 9 | loose: true, 10 | root: '../../' 11 | }) 12 | ], 13 | // build: { 14 | // commonjsOptions: { 15 | // // include: [/@repo-ui/, /node_modules/], 16 | // include: [/@ui/, /@repo-ui/, /node_modules/], 17 | // }, 18 | // }, 19 | // server: { 20 | // fs: { 21 | // // Allow serving files from two level up to the project root 22 | // allow: ['../..'], 23 | // }, 24 | // } 25 | }); 26 | -------------------------------------------------------------------------------- /packages/ui/components/ui/card/index.ts: -------------------------------------------------------------------------------- 1 | import Root from './card.svelte'; 2 | import Content from './card-content.svelte'; 3 | import Description from './card-description.svelte'; 4 | import Footer from './card-footer.svelte'; 5 | import Header from './card-header.svelte'; 6 | import Title from './card-title.svelte'; 7 | 8 | export { 9 | Root, 10 | Content, 11 | Description, 12 | Footer, 13 | Header, 14 | Title, 15 | // 16 | Root as Card, 17 | Content as CardContent, 18 | Description as CardDescription, 19 | Footer as CardFooter, 20 | Header as CardHeader, 21 | Title as CardTitle 22 | }; 23 | 24 | export type HeadingLevel = 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6'; 25 | -------------------------------------------------------------------------------- /apps/launcher/src/lib/command/footers/new-stash-groups.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 9 | Toggle groups 10 | 11 | 12 | 13 | 14 | Create stash 15 | 16 | 17 | -------------------------------------------------------------------------------- /packages/ui/components/ui/command/command-group.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/ui/components/ui/alert-dialog/alert-dialog-action.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /packages/ui/components/ui/dialog/dialog-overlay.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 22 | -------------------------------------------------------------------------------- /packages/ui/postcss.config.cjs: -------------------------------------------------------------------------------- 1 | // const tailwindcss = require('tailwindcss'); 2 | // const autoprefixer = require('autoprefixer'); 3 | // const nested = require('tailwindcss/nesting'); 4 | 5 | // const config = { 6 | // plugins: [ 7 | // nested, 8 | // //Some plugins, like tailwindcss/nesting, need to run before Tailwind, 9 | // tailwindcss(), 10 | // //But others, like autoprefixer, need to run after, 11 | // autoprefixer 12 | // ] 13 | // }; 14 | 15 | // module.exports = config; 16 | 17 | /** @type {import('postcss-load-config').Config} */ 18 | module.exports = { 19 | plugins: [ 20 | require('tailwindcss/nesting'), 21 | require('tailwindcss'), 22 | require('autoprefixer'), 23 | ] 24 | }; -------------------------------------------------------------------------------- /packages/tsconfig/tsconfig-base.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://json.schemastore.org/tsconfig", 3 | "display": "Default", 4 | "compilerOptions": { 5 | "composite": false, 6 | "declaration": true, 7 | "declarationMap": true, 8 | "esModuleInterop": true, 9 | "forceConsistentCasingInFileNames": true, 10 | "inlineSources": false, 11 | "isolatedModules": true, 12 | "moduleResolution": "node", 13 | "noUnusedLocals": false, 14 | "noUnusedParameters": false, 15 | "preserveWatchOutput": true, 16 | "skipLibCheck": true, 17 | "strict": true 18 | }, 19 | "exclude": [ 20 | "node_modules" 21 | ] 22 | } -------------------------------------------------------------------------------- /apps/extension/vite.config.ts: -------------------------------------------------------------------------------- 1 | import { defineConfig, loadEnv } from "vite"; 2 | import { svelte } from "@sveltejs/vite-plugin-svelte"; 3 | import webExtension from "@samrum/vite-plugin-web-extension"; 4 | import { getManifest } from "./src/manifest"; 5 | import tsconfigPaths from 'vite-tsconfig-paths'; 6 | 7 | // https://vitejs.dev/config/ 8 | export default defineConfig(({ mode }) => { 9 | const env = loadEnv(mode, process.cwd(), ""); 10 | 11 | return { 12 | plugins: [ 13 | svelte(), 14 | webExtension({ 15 | manifest: getManifest(Number(env.MANIFEST_VERSION)) 16 | }), 17 | tsconfigPaths({ 18 | loose: true, 19 | root: '../../' 20 | }) 21 | ], 22 | }; 23 | }); -------------------------------------------------------------------------------- /packages/ui/components/ui/alert-dialog/alert-dialog-cancel.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /packages/ui/components/ui/alert-dialog/alert-dialog-overlay.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 22 | -------------------------------------------------------------------------------- /apps/web/src/lib/components/site/logos/index.ts: -------------------------------------------------------------------------------- 1 | import Arc from './arc.svelte'; 2 | import Behance from './behance.svelte'; 3 | import Chrome from './chrome.svelte'; 4 | import Codepen from './codepen.svelte'; 5 | import Dribbble from './dribbble.svelte'; 6 | import GithubStar from './github-star.svelte'; 7 | import TwitterBookmark from './twitter-bookmark.svelte'; 8 | import Colors from './colors.svelte'; 9 | import Image from './image.svelte'; 10 | import Text from './text.svelte'; 11 | 12 | import LogoPoof from './logo-poof.svelte'; 13 | 14 | export { 15 | Arc, 16 | Behance, 17 | Chrome, 18 | Codepen, 19 | Dribbble, 20 | GithubStar, 21 | TwitterBookmark, 22 | LogoPoof, 23 | Colors, 24 | Image, 25 | Text, 26 | } -------------------------------------------------------------------------------- /apps/web/src/routes/+error.svelte: -------------------------------------------------------------------------------- 1 | 9 | 10 | 11 | 12 | 404 Error 13 | Page not found! 14 | {#if $page.data.user} 15 | Go back 16 | {:else} 17 | Go back 18 | {/if} 19 | 20 | 21 | -------------------------------------------------------------------------------- /packages/constants/siteconfig.js: -------------------------------------------------------------------------------- 1 | export const siteConfig = /** @type {const} */ ({ 2 | "name": "Stashlist", 3 | "slogan": "Your Creative Vault", 4 | "appUrl": "/main", 5 | "url": "https://stashlist.app", 6 | "wwwUrl": "https://www.stashlist.app", 7 | "fullAppUrl": "https://stashlist.app/main", 8 | "ogImage": "https://stashlist.app/og.png", 9 | "description": "The creative vault for developers and designers.", 10 | "keywords": "bookmarks, bookmark tool, code snippets, inspiration, stashlist, raindrop alternative, bookmark developers, raindrop developers", 11 | "extensionUrl": { 12 | "chrome": "https://chromewebstore.google.com/detail/flpkpkopodpmfiobdcdhffgphjibolka" 13 | }, 14 | "releaseUrl": "https://github.com/jramke/stashlist/releases/latest" 15 | }); -------------------------------------------------------------------------------- /apps/web/src/lib/stores/index.ts: -------------------------------------------------------------------------------- 1 | import { persisted } from 'svelte-persisted-store'; 2 | import { writable } from 'svelte/store'; 3 | 4 | import { newStashStore } from './new-stash'; 5 | import { itemsStore } from './items'; 6 | 7 | export const listColumns = persisted('stashlist-columns', 3); 8 | 9 | type ListLayout = 'grid' | 'list' | 'masonry'; 10 | export const listLayout = persisted('stashlist-layout', 'grid'); 11 | 12 | export const commandMenuOpen = writable(false); 13 | 14 | export const liveView = writable(false); 15 | 16 | export const editGroupsDialogOpen = writable(false); 17 | 18 | export const newGroupDialogOpen = writable(false); 19 | 20 | export const generateKeyDialogOpen = writable(false); 21 | 22 | export { newStashStore, itemsStore }; -------------------------------------------------------------------------------- /packages/config-eslint/index.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: [ 4 | 'eslint:recommended', 5 | 'plugin:@typescript-eslint/recommended', 6 | 'plugin:svelte/recommended', 7 | 'prettier', 8 | 'turbo' 9 | ], 10 | parser: '@typescript-eslint/parser', 11 | plugins: ['@typescript-eslint'], 12 | parserOptions: { 13 | sourceType: 'module', 14 | ecmaVersion: 2020, 15 | extraFileExtensions: ['.svelte'] 16 | }, 17 | env: { 18 | browser: true, 19 | es2017: true, 20 | node: true 21 | }, 22 | overrides: [ 23 | { 24 | files: ['*.svelte'], 25 | parser: 'svelte-eslint-parser', 26 | parserOptions: { 27 | parser: '@typescript-eslint/parser' 28 | } 29 | } 30 | ] 31 | }; 32 | -------------------------------------------------------------------------------- /packages/ui/components/ui/button/button.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /apps/extension/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "@tsconfig/svelte/tsconfig.json", 3 | "compilerOptions": { 4 | "allowSyntheticDefaultImports": true, 5 | "target": "esnext", 6 | "useDefineForClassFields": true, 7 | "module": "esnext", 8 | "resolveJsonModule": true, 9 | "baseUrl": ".", 10 | "paths": { 11 | "@/*": ["./src/*"], 12 | }, 13 | /** 14 | * Typecheck JS in `.svelte` and `.js` files by default. 15 | * Disable checkJs if you'd like to use dynamic types in JS. 16 | * Note that setting allowJs false does not prevent the use 17 | * of JS in `.svelte` files. 18 | */ 19 | "allowJs": true, 20 | "checkJs": true 21 | }, 22 | // "include": ["*"] 23 | "include": ["src/**/*.d.ts", "src/**/*.ts", "src/**/*.js", "src/**/*.svelte"] 24 | } 25 | -------------------------------------------------------------------------------- /packages/ui/components/ui/separator/separator.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 23 | -------------------------------------------------------------------------------- /apps/web/src/lib/components/app/saves/item/types/item-color.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | {#if description} 18 | 19 | {description} 20 | 21 | {/if} 22 | -------------------------------------------------------------------------------- /packages/ui/components/ui/context-menu/context-menu-content.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /apps/launcher/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import { sveltekit } from "@sveltejs/kit/vite"; 3 | import tsconfigPaths from 'vite-tsconfig-paths'; 4 | 5 | // https://vitejs.dev/config/ 6 | export default defineConfig({ 7 | plugins: [ 8 | sveltekit(), 9 | tsconfigPaths({ 10 | loose: true, 11 | root: '../../' 12 | }) 13 | ], 14 | 15 | // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` 16 | // 17 | // 1. prevent vite from obscuring rust errors 18 | clearScreen: false, 19 | // 2. tauri expects a fixed port, fail if that port is not available 20 | server: { 21 | port: 1420, 22 | strictPort: true, 23 | watch: { 24 | // 3. tell vite to ignore watching `src-tauri` 25 | ignored: ["**/src-tauri/**"], 26 | }, 27 | }, 28 | }); 29 | -------------------------------------------------------------------------------- /packages/ui/components/ui/form/form-field-errors.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 21 | 22 | {#each errors as error} 23 | {error} 24 | {/each} 25 | 26 | 27 | -------------------------------------------------------------------------------- /packages/ui/components/ui/dropdown-menu/dropdown-menu-content.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /packages/ui/components/ui/command/command-breadcrumbs.svelte: -------------------------------------------------------------------------------- 1 | 10 | 11 | 12 | {#each pages as page, i} 13 | onPageClick(page)} 16 | on:keydown={(e) => { 17 | if (e.key === 'Enter') { 18 | e.preventDefault(); 19 | onPageClick(page); 20 | } 21 | }} 22 | > 23 | {page} 24 | 25 | {/each} 26 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "scripts": { 4 | "build": "turbo run build", 5 | "dev": "turbo run dev", 6 | "preview": "turbo run preview", 7 | "lint": "turbo run lint", 8 | "format": "prettier --write .", 9 | "ui:add": "pnpm --filter @repo/ui ui:add", 10 | "turso": "pnpm --filter web turso", 11 | "studio": "pnpm --filter web studio", 12 | "generate": "pnpm --filter web generate", 13 | "migrate": "pnpm --filter web migrate", 14 | "push": "pnpm --filter web push", 15 | "introspect": "pnpm --filter web introspect" 16 | }, 17 | "devDependencies": { 18 | "@repo/eslint-config": "workspace:*", 19 | "eslint": "^8.57.1", 20 | "prettier": "^3.4.2", 21 | "prettier-plugin-svelte": "^3.3.2", 22 | "turbo": "^1.13.4" 23 | }, 24 | "engines": { 25 | "node": ">=18" 26 | }, 27 | "packageManager": "pnpm@9.2.0" 28 | } 29 | -------------------------------------------------------------------------------- /packages/ui/components/ui/input/index.ts: -------------------------------------------------------------------------------- 1 | import Root from './input.svelte'; 2 | 3 | type FormInputEvent = T & { 4 | currentTarget: EventTarget & HTMLInputElement; 5 | }; 6 | export type InputEvents = { 7 | blur: FormInputEvent; 8 | change: FormInputEvent; 9 | click: FormInputEvent; 10 | focus: FormInputEvent; 11 | focusin: FormInputEvent; 12 | focusout: FormInputEvent; 13 | keydown: FormInputEvent; 14 | keypress: FormInputEvent; 15 | keyup: FormInputEvent; 16 | mouseover: FormInputEvent; 17 | mouseenter: FormInputEvent; 18 | mouseleave: FormInputEvent; 19 | paste: FormInputEvent; 20 | input: FormInputEvent; 21 | }; 22 | 23 | export { 24 | Root, 25 | // 26 | Root as Input 27 | }; 28 | -------------------------------------------------------------------------------- /apps/web/src/routes/main/group/new/+page.server.ts: -------------------------------------------------------------------------------- 1 | import type { Actions } from './$types'; 2 | 3 | import { superValidate, message } from 'sveltekit-superforms/server'; 4 | import { zod } from 'sveltekit-superforms/adapters'; 5 | import { formSchema } from './schema'; 6 | 7 | export const actions: Actions = { 8 | default: async (event) => { 9 | const form = await superValidate(event, zod(formSchema)); 10 | 11 | if (!form.valid) { 12 | return message(form, { type: 'error', text: 'Something went wrong. Please try again.' }); 13 | } 14 | 15 | try { 16 | const response = await event.fetch('/api/groups/new', { 17 | method: 'POST', 18 | body: JSON.stringify(form.data) 19 | }); 20 | } catch (error) { 21 | console.log('Error creating new group', error); 22 | return message(form, { type: 'error', text: 'Something went wrong. Please try again.' }); 23 | } 24 | } 25 | }; 26 | -------------------------------------------------------------------------------- /packages/ui/components/ui/tooltip/tooltip-content.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /apps/web/src/lib/components/site/hero/vault-clip-path.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /packages/ui/components/ui/form/form-fieldset.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 19 | 20 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /apps/extension/src/entries/contentScript/utils/index.ts: -------------------------------------------------------------------------------- 1 | export function waitForElement( 2 | selector: string, 3 | root: HTMLElement | null | undefined | Document = document, 4 | timeout: number = 5000 5 | ): Promise { 6 | return new Promise((resolve, reject) => { 7 | const interval = setInterval(() => { 8 | if (!root) { 9 | clearInterval(interval); 10 | clearTimeout(timer); 11 | reject(new Error('Root element not found')); 12 | } 13 | const element = root!.querySelector(selector); 14 | if (element) { 15 | clearInterval(interval); 16 | clearTimeout(timer); 17 | resolve(element); 18 | } 19 | }, 100); 20 | 21 | const timer = setTimeout(() => { 22 | clearInterval(interval); 23 | reject(new Error(`Element ${selector} not found within ${timeout}ms`)); 24 | }, timeout); 25 | }); 26 | } 27 | -------------------------------------------------------------------------------- /packages/ui/components/ui/textarea/index.ts: -------------------------------------------------------------------------------- 1 | import Root from './textarea.svelte'; 2 | 3 | type FormTextareaEvent = T & { 4 | currentTarget: EventTarget & HTMLTextAreaElement; 5 | }; 6 | 7 | type TextareaEvents = { 8 | blur: FormTextareaEvent; 9 | change: FormTextareaEvent; 10 | click: FormTextareaEvent; 11 | focus: FormTextareaEvent; 12 | keydown: FormTextareaEvent; 13 | keypress: FormTextareaEvent; 14 | keyup: FormTextareaEvent; 15 | mouseover: FormTextareaEvent; 16 | mouseenter: FormTextareaEvent; 17 | mouseleave: FormTextareaEvent; 18 | paste: FormTextareaEvent; 19 | input: FormTextareaEvent; 20 | }; 21 | 22 | export { 23 | Root, 24 | // 25 | Root as Textarea, 26 | type TextareaEvents, 27 | type FormTextareaEvent 28 | }; 29 | -------------------------------------------------------------------------------- /packages/ui/components/ui/select/index.ts: -------------------------------------------------------------------------------- 1 | import { Select as SelectPrimitive } from 'bits-ui'; 2 | 3 | import Label from './select-label.svelte'; 4 | import Item from './select-item.svelte'; 5 | import Content from './select-content.svelte'; 6 | import Trigger from './select-trigger.svelte'; 7 | import Separator from './select-separator.svelte'; 8 | 9 | const Root = SelectPrimitive.Root; 10 | const Group = SelectPrimitive.Group; 11 | const Input = SelectPrimitive.Input; 12 | const Value = SelectPrimitive.Value; 13 | 14 | export { 15 | Root, 16 | Group, 17 | Input, 18 | Label, 19 | Item, 20 | Value, 21 | Content, 22 | Trigger, 23 | Separator, 24 | // 25 | Root as Select, 26 | Group as SelectGroup, 27 | Input as SelectInput, 28 | Label as SelectLabel, 29 | Item as SelectItem, 30 | Value as SelectValue, 31 | Content as SelectContent, 32 | Trigger as SelectTrigger, 33 | Separator as SelectSeparator 34 | }; 35 | -------------------------------------------------------------------------------- /packages/ui/components/ui/badge/index.ts: -------------------------------------------------------------------------------- 1 | import { tv, type VariantProps } from "tailwind-variants"; 2 | export { default as Badge } from "./badge.svelte"; 3 | 4 | export const badgeVariants = tv({ 5 | base: "inline-flex items-center border rounded-full px-2.5 py-0.5 text-xs transition-colors focus:outline-none select-none focus:ring-2 focus:ring-ring focus:ring-offset-2", 6 | variants: { 7 | variant: { 8 | default: "bg-accent shadow-inner shadow-popover border border-accent text-muted-foreground", 9 | secondary: 10 | "bg-secondary hover:bg-secondary/80 border-transparent text-secondary-foreground", 11 | destructive: 12 | "bg-destructive hover:bg-destructive/80 border-transparent text-destructive-foreground", 13 | outline: "text-foreground", 14 | }, 15 | }, 16 | defaultVariants: { 17 | variant: "default", 18 | }, 19 | }); 20 | 21 | export type Variant = VariantProps["variant"]; 22 | -------------------------------------------------------------------------------- /packages/ui/components/ui/context-menu/context-menu-sub-content.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /apps/web/src/routes/api/connect/generate-key/+server.ts: -------------------------------------------------------------------------------- 1 | import { generateId } from 'lucia'; 2 | import type { RequestHandler } from './$types'; 3 | import { error, json, redirect } from '@sveltejs/kit'; 4 | import { updateUser } from '$lib/server/db/queries'; 5 | import { Argon2id } from "oslo/password"; 6 | 7 | export const POST: RequestHandler = async ({ locals }) => { 8 | if (!locals.user) redirect(302, '/login'); 9 | 10 | const encodedUserName = encodeURIComponent(locals.user.username); 11 | const apiKey = encodedUserName + '-' + generateId(30); 12 | 13 | const argon2id = new Argon2id(); 14 | const hash = await argon2id.hash(apiKey); 15 | 16 | await updateUser(locals.user.id, { apiKeyHash: hash }); 17 | 18 | try { 19 | return json({ apiKey }); 20 | } catch (err) { 21 | console.log('error generating api key', err); 22 | return error(400, { 23 | message: 'Failed to generate API Key' 24 | }); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /apps/launcher/src-tauri/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "Stashlist_Launcher" 3 | version = "0.1.0" 4 | description = "Stashlist Launcher" 5 | authors = ["you"] 6 | edition = "2021" 7 | 8 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html 9 | 10 | [build-dependencies] 11 | tauri-build = { version = "2.0.0-beta", features = [] } 12 | 13 | [dependencies] 14 | tauri = { version = "2.0.0-beta", features = [] } 15 | tauri-plugin-shell = "2.0.0-beta" 16 | serde = { version = "1", features = ["derive"] } 17 | serde_json = "1" 18 | tauri-plugin-http = "2.0.0-beta.9" 19 | tauri-plugin-stronghold = "2.0.0-beta.6" 20 | rust-argon2 = "2.1.0" 21 | tauri-plugin-clipboard-manager = "2.1.0-beta.4" 22 | dotenvs = "0.1.0" 23 | 24 | [target."cfg(not(any(target_os = \"android\", target_os = \"ios\")))".dependencies] 25 | tauri-plugin-autostart = "2.0.0-beta" 26 | tauri-plugin-global-shortcut = "2.0.0-beta" 27 | 28 | -------------------------------------------------------------------------------- /packages/ui/components/ui/command/command-item.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /apps/web/src/lib/server/db/migrate.ts: -------------------------------------------------------------------------------- 1 | import { migrate } from 'drizzle-orm/libsql/migrator'; 2 | import { drizzle } from 'drizzle-orm/libsql'; 3 | import { createClient } from '@libsql/client'; 4 | import * as dotenv from 'dotenv'; 5 | dotenv.config(); 6 | 7 | const { DATABASE_URL, DATABASE_AUTH_TOKEN } = process.env; 8 | if (!DATABASE_URL) { 9 | throw new Error('No url'); 10 | } 11 | if (!DATABASE_AUTH_TOKEN) { 12 | throw new Error('No auth token'); 13 | } 14 | 15 | const client = createClient({ url: DATABASE_URL as string, authToken: DATABASE_AUTH_TOKEN as string }); 16 | const db = drizzle(client); 17 | 18 | async function main() { 19 | try { 20 | console.log('Migration started...'); 21 | await migrate(db, { 22 | migrationsFolder: 'drizzle' 23 | }); 24 | console.log('Tables migrated!'); 25 | process.exit(0); 26 | } catch (error) { 27 | console.error('Error performing migration: ', error); 28 | process.exit(1); 29 | } 30 | } 31 | main(); -------------------------------------------------------------------------------- /apps/web/src/app.d.ts: -------------------------------------------------------------------------------- 1 | // See https://kit.svelte.dev/docs/types#app 2 | 3 | import type { TODO } from '$lib/types'; 4 | 5 | // for information about these interfaces 6 | declare global { 7 | namespace App { 8 | // interface Error {} 9 | interface Locals { 10 | user: import('lucia').User | null; 11 | session: import('lucia').Session | null; 12 | } 13 | // interface PageData {} 14 | interface PageState { 15 | editStash?: { 16 | form: import('sveltekit-superforms').SuperValidated | null | string; 17 | save: TODO; 18 | groups: TODO; 19 | isDialog: boolean; 20 | }; 21 | editGroup?: { 22 | form: import('sveltekit-superforms').SuperValidated | null | string; 23 | groups: TODO; 24 | isDialog: boolean; 25 | }; 26 | } 27 | // interface Platform {} 28 | namespace Superforms { 29 | type Message = { 30 | type: 'error' | 'success'; 31 | text: string; 32 | }; 33 | } 34 | } 35 | } 36 | 37 | export {}; 38 | -------------------------------------------------------------------------------- /packages/ui/components/ui/form/form-field.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /apps/web/src/routes/+layout.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 18 | 19 | 20 | 21 | 22 | 23 | {#if !$page.route?.id?.includes(siteConfig.appUrl)} 24 | 25 | {/if} 26 | 27 | 28 | 29 | 30 | 31 | {#if !$page.route?.id?.includes(siteConfig.appUrl)} 32 | 33 | {/if} 34 | -------------------------------------------------------------------------------- /packages/ui/components/ui/textarea/textarea.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 32 | -------------------------------------------------------------------------------- /apps/web/src/routes/api/groups/new/+server.ts: -------------------------------------------------------------------------------- 1 | import type { RequestHandler } from './$types'; 2 | 3 | import { json, redirect, error } from '@sveltejs/kit'; 4 | import { getRandomIndex } from '$lib/utils'; 5 | import { gradients } from '@repo/constants'; 6 | import { createNewGroup } from '$lib/server/db/queries'; 7 | 8 | export const POST: RequestHandler = async ({ request, locals }) => { 9 | if (!locals.user) redirect(302, '/login'); 10 | 11 | const data = await request.json(); 12 | 13 | try { 14 | const title = data['title']; 15 | if (title.length === 0) throw Error('Group title cant be empty'); 16 | 17 | const gradientIndex = data['gradientIndex'] ?? getRandomIndex(gradients); 18 | 19 | await createNewGroup(locals.user.id, title, gradientIndex); 20 | 21 | return json({ success: true }); 22 | } catch (err) { 23 | console.log('error create group', err); 24 | // return json({ success: false }); 25 | return error(400, 'Failed to create group'); 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /packages/ui/components/ui/form/index.ts: -------------------------------------------------------------------------------- 1 | import * as FormPrimitive from "formsnap"; 2 | import Description from "./form-description.svelte"; 3 | import Label from "./form-label.svelte"; 4 | import FieldErrors from "./form-field-errors.svelte"; 5 | import Field from "./form-field.svelte"; 6 | import Fieldset from "./form-fieldset.svelte"; 7 | import Legend from "./form-legend.svelte"; 8 | import ElementField from "./form-element-field.svelte"; 9 | import Button from "./form-button.svelte"; 10 | 11 | const Control = FormPrimitive.Control; 12 | 13 | export { 14 | Field, 15 | Control, 16 | Label, 17 | Button, 18 | FieldErrors, 19 | Description, 20 | Fieldset, 21 | Legend, 22 | ElementField, 23 | // 24 | Field as FormField, 25 | Control as FormControl, 26 | Description as FormDescription, 27 | Label as FormLabel, 28 | FieldErrors as FormFieldErrors, 29 | Fieldset as FormFieldset, 30 | Legend as FormLegend, 31 | ElementField as FormElementField, 32 | Button as FormButton, 33 | }; 34 | -------------------------------------------------------------------------------- /packages/ui/components/ui/dropdown-menu/dropdown-menu-sub-content.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /packages/ui/components/ui/scroll-area/scroll-area-scrollbar.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 23 | 24 | 27 | 28 | -------------------------------------------------------------------------------- /packages/ui/components/ui/select/select-trigger.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /packages/ui/components/ui/sonner/sonner.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 26 | -------------------------------------------------------------------------------- /packages/ui/components/ui/alert-dialog/alert-dialog-content.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /packages/ui/components/ui/dialog/index.ts: -------------------------------------------------------------------------------- 1 | import { Dialog as DialogPrimitive } from 'bits-ui'; 2 | 3 | // const Root = DialogPrimitive.Root; 4 | const Trigger = DialogPrimitive.Trigger; 5 | 6 | import Root from './dialog.svelte'; 7 | import Title from './dialog-title.svelte'; 8 | import Portal from './dialog-portal.svelte'; 9 | import Footer from './dialog-footer.svelte'; 10 | import Header from './dialog-header.svelte'; 11 | import Overlay from './dialog-overlay.svelte'; 12 | import Content from './dialog-content.svelte'; 13 | import Description from './dialog-description.svelte'; 14 | 15 | export { 16 | Root, 17 | Title, 18 | Portal, 19 | Footer, 20 | Header, 21 | Trigger, 22 | Overlay, 23 | Content, 24 | Description, 25 | // 26 | Root as Dialog, 27 | Title as DialogTitle, 28 | Portal as DialogPortal, 29 | Footer as DialogFooter, 30 | Header as DialogHeader, 31 | Trigger as DialogTrigger, 32 | Overlay as DialogOverlay, 33 | Content as DialogContent, 34 | Description as DialogDescription 35 | }; 36 | -------------------------------------------------------------------------------- /packages/ui/components/ui/form/form-element-field.svelte: -------------------------------------------------------------------------------- 1 | 6 | 7 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /apps/web/src/routes/main/save/new/+page.server.ts: -------------------------------------------------------------------------------- 1 | import type { Actions } from './$types'; 2 | 3 | import { superValidate, message } from 'sveltekit-superforms/server'; 4 | import { zod } from 'sveltekit-superforms/adapters'; 5 | import { formSchema } from './schema'; 6 | 7 | export const actions: Actions = { 8 | default: async (event) => { 9 | const form = await superValidate(event, zod(formSchema)); 10 | 11 | if (!form.valid) { 12 | return message(form, { type: 'error', text: 'Something went wrong. Please try again.' }); 13 | } 14 | 15 | try { 16 | const response = await event.fetch('/api/saves/new', { 17 | method: 'POST', 18 | body: JSON.stringify(form.data) 19 | }); 20 | 21 | if (!response.ok) { 22 | throw new Error(`Error creating save. Status: ${response.status}`); 23 | } 24 | 25 | } catch (error) { 26 | console.log('Error creating new save', error); 27 | return message(form, { type: 'error', text: 'Something went wrong. Please try again.' }); 28 | } 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /packages/ui/components/ui/command/command.svelte: -------------------------------------------------------------------------------- 1 | 12 | 13 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /apps/web/src/routes/(auth)/login/+page.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 16 | 17 | 18 | Login 19 | Your digital vault awaits – let's get started! 20 | 21 | 22 | 23 | By logging in, you agree to our{' '} 24 | 25 | Privacy Policy 26 | 27 | . 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /apps/web/src/routes/main/group/delete/+page.server.ts: -------------------------------------------------------------------------------- 1 | import type { Actions } from './$types'; 2 | 3 | import { superValidate, message } from 'sveltekit-superforms/server'; 4 | import { zod } from 'sveltekit-superforms/adapters'; 5 | import { formSchema } from './schema'; 6 | import { redirect } from '@sveltejs/kit'; 7 | import { siteConfig } from '@repo/constants'; 8 | import { deleteGroup } from '$lib/server/db/queries'; 9 | 10 | export const actions: Actions = { 11 | default: async (event) => { 12 | const form = await superValidate(event, zod(formSchema)); 13 | 14 | if (!form.valid) { 15 | return message(form, { type: 'error', text: 'Something went wrong. Please try again.' }); 16 | } 17 | 18 | try { 19 | await deleteGroup(form.data.id); 20 | 21 | } catch (error) { 22 | console.log('Error deleting group', error); 23 | return message(form, { type: 'error', text: 'Something went wrong. Please try again.' }); 24 | } 25 | 26 | if (form.data.isOnCurrentSlug) { 27 | redirect(301, siteConfig.appUrl); 28 | } 29 | } 30 | }; 31 | -------------------------------------------------------------------------------- /apps/web/src/routes/(auth)/login/google/+server.ts: -------------------------------------------------------------------------------- 1 | import type { RequestEvent } from "@sveltejs/kit"; 2 | 3 | import { google } from "$lib/server/auth"; 4 | import { generateState, generateCodeVerifier } from "arctic"; 5 | import { redirect } from "@sveltejs/kit"; 6 | 7 | export async function GET(event: RequestEvent): Promise { 8 | const state = generateState(); 9 | const codeVerifier = generateCodeVerifier(); 10 | const url = await google.createAuthorizationURL(state, codeVerifier, { 11 | scopes: ['profile'] 12 | }); 13 | 14 | // store state verifier as cookie 15 | event.cookies.set('google_oauth_state', state, { 16 | secure: import.meta.env.PROD, 17 | sameSite: 'lax', 18 | path: '/', 19 | httpOnly: true, 20 | maxAge: 60 * 10 // 10 min 21 | }); 22 | 23 | // store code verifier as cookie 24 | event.cookies.set('google_oauth_code_verifier', codeVerifier, { 25 | secure: import.meta.env.PROD, 26 | sameSite: 'lax', 27 | path: '/', 28 | httpOnly: true, 29 | maxAge: 60 * 10 // 10 min 30 | }); 31 | 32 | return redirect(302, url.toString()); 33 | } -------------------------------------------------------------------------------- /packages/ui/components/ui/radio-group/radio-group-item.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /apps/web/src/lib/components/app/saves/item/types/item-text.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | {#if text && title} 21 | 22 | {text} 23 | 24 | {/if} 25 | -------------------------------------------------------------------------------- /apps/web/src/routes/api/explore/explore.json: -------------------------------------------------------------------------------- 1 | [ 2 | "https://www.webbyawards.com/", 3 | "https://www.freeject.net/search?q=ripped+paper", 4 | "https://www.uidesigndaily.com/", 5 | "https://freefrontend.com/", 6 | "https://dribbble.com/shots/popular/web-design", 7 | "https://land-book.com/", 8 | "https://onepagelove.com/", 9 | "https://landingfolio.com/", 10 | "https://www.lapa.ninja/", 11 | "https://godly.website/", 12 | "https://www.awwwards.com/websites/", 13 | "https://www.siteinspire.com/", 14 | "https://mindsparklemag.com/category/website/", 15 | "https://minimal.gallery/", 16 | "https://the-responsive.com/", 17 | "https://webflow.com/made-in-webflow", 18 | "https://www.landing.love/", 19 | "https://bestwebsite.gallery/sites", 20 | "https://httpster.net/", 21 | "https://www.darkmodedesign.com/", 22 | "https://siiimple.com/", 23 | "https://www.doingcoolstuff.xyz/", 24 | "https://www.curated.design/", 25 | "https://www.toools.design/", 26 | "https://calltoinspiration.com/", 27 | "https://www.ogimage.gallery/" 28 | ] 29 | -------------------------------------------------------------------------------- /packages/ui/tsconfig.json: -------------------------------------------------------------------------------- 1 | // { 2 | // "extends": "@repo/tsconfig/tsconfig-svelte-library.json", 3 | // "compilerOptions": { 4 | // "baseUrl": ".", 5 | // "paths": { 6 | // "$ui/components": ["./components"], 7 | // "$ui/components/*": ["./components/*"], 8 | // "$ui/utils": ["./utils"], 9 | // "$ui/utils/*": ["./utils/*"] 10 | // } 11 | // }, 12 | // "include": ["."], 13 | // "exclude": ["node_modules"] 14 | // } 15 | { 16 | "extends": "@tsconfig/svelte/tsconfig.json", 17 | "include": ["."], 18 | "exclude": ["node_modules"], 19 | "compilerOptions": { 20 | "baseUrl": ".", 21 | // "allowJs": true, 22 | "checkJs": false, 23 | "isolatedModules": true, 24 | "resolveJsonModule": true, 25 | "strict": true, 26 | "target": "esnext", 27 | "module": "esnext", 28 | "moduleResolution": "node", 29 | "ignoreDeprecations": "5.0", 30 | "lib": ["esnext", "DOM", "DOM.Iterable"], 31 | "paths": { 32 | "$ui/components": ["./components"], 33 | "$ui/components/*": ["./components/*"], 34 | "$ui/utils": ["./utils"], 35 | "$ui/utils/*": ["./utils/*"] 36 | } 37 | } 38 | } -------------------------------------------------------------------------------- /apps/web/src/lib/stores/new-stash.ts: -------------------------------------------------------------------------------- 1 | import { get, writable } from "svelte/store"; 2 | import { commandMenuOpen } from "."; 3 | 4 | const newStashInput = writable(null); 5 | 6 | type NewStashStore = { 7 | focusNewStashInput: typeof focusNewStashInput; 8 | setNewStashInput: typeof setNewStashInput; 9 | newStashInput: typeof newStashInput; 10 | }; 11 | 12 | function setNewStashInput(input: HTMLInputElement) { 13 | newStashInput.set(input); 14 | } 15 | 16 | function focusNewStashInput() { 17 | const focusIt = () => { 18 | setTimeout(() => { 19 | newStashInput.update($input => { 20 | $input?.focus(); 21 | return $input; 22 | }); 23 | }); 24 | } 25 | if (get(commandMenuOpen) === true) { 26 | commandMenuOpen.set(false); 27 | setTimeout(focusIt, 250); // need to wait for the menu to close, because it blurs the focus on close 28 | } else { 29 | focusIt(); 30 | } 31 | } 32 | 33 | export const newStashStore: NewStashStore = { 34 | focusNewStashInput, 35 | setNewStashInput, 36 | newStashInput, 37 | }; -------------------------------------------------------------------------------- /packages/ui/components/ui/command/command-input.svelte: -------------------------------------------------------------------------------- 1 | 17 | 18 | 19 | 20 | 21 | 29 | 30 | -------------------------------------------------------------------------------- /packages/ui/components/ui/input/input.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 36 | -------------------------------------------------------------------------------- /apps/web/src/lib/components/app/saves/item/types/item-image.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | {#if description} 27 | 28 | {description} 29 | 30 | {/if} 31 | -------------------------------------------------------------------------------- /packages/ui/components/ui/drawer/index.ts: -------------------------------------------------------------------------------- 1 | import { Drawer as DrawerPrimitive } from "vaul-svelte"; 2 | 3 | import Root from "./drawer.svelte"; 4 | import Content from "./drawer-content.svelte"; 5 | import Description from "./drawer-description.svelte"; 6 | import Overlay from "./drawer-overlay.svelte"; 7 | import Footer from "./drawer-footer.svelte"; 8 | import Header from "./drawer-header.svelte"; 9 | import Title from "./drawer-title.svelte"; 10 | import NestedRoot from "./drawer-nested.svelte"; 11 | 12 | const Trigger = DrawerPrimitive.Trigger; 13 | const Portal = DrawerPrimitive.Portal; 14 | const Close = DrawerPrimitive.Close; 15 | 16 | export { 17 | Root, 18 | NestedRoot, 19 | Content, 20 | Description, 21 | Overlay, 22 | Footer, 23 | Header, 24 | Title, 25 | Trigger, 26 | Portal, 27 | Close, 28 | 29 | // 30 | Root as Drawer, 31 | NestedRoot as DrawerNestedRoot, 32 | Content as DrawerContent, 33 | Description as DrawerDescription, 34 | Overlay as DrawerOverlay, 35 | Footer as DrawerFooter, 36 | Header as DrawerHeader, 37 | Title as DrawerTitle, 38 | Trigger as DrawerTrigger, 39 | Portal as DrawerPortal, 40 | Close as DrawerClose, 41 | }; 42 | -------------------------------------------------------------------------------- /apps/web/src/lib/components/site/logos/text.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /packages/ui/components/ui/context-menu/context-menu-item.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/ui/components/ui/dropdown-menu/dropdown-menu-item.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /apps/launcher/src-tauri/tauri.conf.json: -------------------------------------------------------------------------------- 1 | { 2 | "productName": "Stashlist Launcher", 3 | "version": "0.1.0", 4 | "identifier": "stashlist.app.launcher", 5 | "build": { 6 | "beforeDevCommand": "pnpm dev", 7 | "devUrl": "http://localhost:1420", 8 | "beforeBuildCommand": "pnpm build", 9 | "frontendDist": "../build" 10 | }, 11 | "app": { 12 | "windows": [ 13 | { 14 | "title": "main", 15 | "width": 620, 16 | "height": 490, 17 | "label": "main", 18 | "decorations": false, 19 | "transparent": true, 20 | "center": true, 21 | "visible": false, 22 | "alwaysOnTop": true, 23 | "focus": true, 24 | "hiddenTitle": true, 25 | "minimizable": false, 26 | "resizable": false, 27 | "maximizable": false, 28 | "skipTaskbar": true, 29 | "shadow": false 30 | } 31 | ], 32 | "security": { 33 | "csp": null 34 | } 35 | }, 36 | "bundle": { 37 | "active": true, 38 | "targets": "all", 39 | "icon": [ 40 | "icons/32x32.png", 41 | "icons/128x128.png", 42 | "icons/128x128@2x.png", 43 | "icons/icon.icns", 44 | "icons/icon.ico" 45 | ] 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /packages/ui/icons/stash.svelte: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 10 | 16 | 18 | 19 | -------------------------------------------------------------------------------- /apps/launcher/src/lib/command/command-list-groups.svelte: -------------------------------------------------------------------------------- 1 | 14 | 15 | {#if groups && groups.length > 0} 16 | 17 | {#each groups as group} 18 | handleItemSelect(siteConfig.fullAppUrl + '/group/' + group.id, () => changePage({ name: group.title, id: 'group-' + group.id, groupId: group.id }))}> 19 | 20 | 21 | 22 | 23 | {group.title} 24 | 25 | 26 | {/each} 27 | 28 | {/if} -------------------------------------------------------------------------------- /packages/ui/components/ui/command/index.ts: -------------------------------------------------------------------------------- 1 | import { Command as CommandPrimitive } from "cmdk-sv"; 2 | 3 | import Root from "./command.svelte"; 4 | import Dialog from "./command-dialog.svelte"; 5 | import Empty from "./command-empty.svelte"; 6 | import Group from "./command-group.svelte"; 7 | import Item from "./command-item.svelte"; 8 | import Input from "./command-input.svelte"; 9 | import List from "./command-list.svelte"; 10 | import Separator from "./command-separator.svelte"; 11 | import Shortcut from "./command-shortcut.svelte"; 12 | import Footer from "./command-footer.svelte"; 13 | import Loading from "./command-loading.svelte"; 14 | import Breadcrumbs from "./command-breadcrumbs.svelte"; 15 | 16 | export { 17 | Root, 18 | Dialog, 19 | Empty, 20 | Group, 21 | Item, 22 | Input, 23 | List, 24 | Separator, 25 | Shortcut, 26 | Loading, 27 | Footer, 28 | Breadcrumbs, 29 | // 30 | Root as Command, 31 | Dialog as CommandDialog, 32 | Empty as CommandEmpty, 33 | Group as CommandGroup, 34 | Item as CommandItem, 35 | Input as CommandInput, 36 | List as CommandList, 37 | Separator as CommandSeparator, 38 | Shortcut as CommandShortcut, 39 | Loading as CommandLoading, 40 | Footer as CommandFooter, 41 | Breadcrumbs as CommandBreadcrumbs, 42 | }; 43 | -------------------------------------------------------------------------------- /apps/extension/src/entries/contentScript/renderContent.ts: -------------------------------------------------------------------------------- 1 | import browser from "webextension-polyfill"; 2 | 3 | export default async function renderContent( 4 | cssPaths: string[], 5 | render: (appRoot: HTMLElement) => void 6 | ) { 7 | const appContainer = document.createElement("div"); 8 | appContainer.id = 'stashlist-container'; 9 | const shadowRoot = appContainer.attachShadow({ 10 | // mode: import.meta.env.MODE === "development" ? "open" : "closed", 11 | mode: "open", 12 | }); 13 | const appRoot = document.createElement("div"); 14 | appRoot.id = 'stashlist-root'; 15 | appRoot.className = 'dark'; 16 | 17 | if (import.meta.hot) { 18 | const { addViteStyleTarget } = await import( 19 | "@samrum/vite-plugin-web-extension/client" 20 | ); 21 | 22 | await addViteStyleTarget(shadowRoot); 23 | } else { 24 | cssPaths.forEach((cssPath: string) => { 25 | const styleEl = document.createElement("link"); 26 | styleEl.setAttribute("rel", "stylesheet"); 27 | styleEl.setAttribute("href", browser.runtime.getURL(cssPath)); 28 | shadowRoot.appendChild(styleEl); 29 | }); 30 | } 31 | 32 | shadowRoot.appendChild(appRoot); 33 | document.body.appendChild(appContainer); 34 | 35 | render(appRoot); 36 | } 37 | -------------------------------------------------------------------------------- /apps/web/src/routes/api/groups/sort/+server.ts: -------------------------------------------------------------------------------- 1 | import type { RequestHandler } from './$types'; 2 | 3 | import { json, redirect, error } from '@sveltejs/kit'; 4 | import { resetGroupSortIndex, updateGroup } from '$lib/server/db/queries'; 5 | 6 | export const POST: RequestHandler = async ({ request, locals, url }) => { 7 | if (!locals.user) redirect(302, '/login'); 8 | 9 | const type = url.searchParams.get('type'); 10 | 11 | try { 12 | if (!type) throw Error('Type searchparam is required'); 13 | 14 | if (type === 'update') { 15 | const data = await request.json(); 16 | 17 | const id = data['id']; 18 | const sortIndex = data['sortIndex']; 19 | 20 | if (!id || typeof sortIndex === 'undefined') throw Error('Group id and sort index are required'); 21 | 22 | await updateGroup(id, { sortIndex }); 23 | return json({ success: true }); 24 | } 25 | 26 | if (type === 'reset') { 27 | await resetGroupSortIndex(locals.user.id); 28 | return json({ success: true }); 29 | } 30 | 31 | throw Error('Invalid type searchparam'); 32 | 33 | } catch (err) { 34 | console.log('error updating sort index for groups', err); 35 | // return json({ success: false }); 36 | return error(400, 'Error updating sort index for groups'); 37 | } 38 | }; 39 | -------------------------------------------------------------------------------- /apps/web/src/lib/components/app/command/command-list-groups.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | {#if groups && groups.length > 0} 18 | 19 | {#each groups as group} 20 | handleItemSelect(siteConfig.appUrl + '/group/' + group.id, () => changePage({ name: group.title, groupId: group.id }))}> 21 | 22 | 23 | 24 | 25 | {group.title} 26 | 27 | 28 | {/each} 29 | 30 | {/if} -------------------------------------------------------------------------------- /apps/launcher/src/lib/command/command-item-image.svelte: -------------------------------------------------------------------------------- 1 | 23 | 24 | {#if imageUrl} 25 | {#if absolute} 26 | 27 | {/if} 28 | imageUrl = ''} 35 | /> 36 | {:else} 37 | 38 | 39 | 40 | {/if} -------------------------------------------------------------------------------- /apps/web/src/routes/api/saves/+server.ts: -------------------------------------------------------------------------------- 1 | import type { RequestHandler } from './$types'; 2 | 3 | import { error, json, redirect } from '@sveltejs/kit'; 4 | import { getSavesWithGroups } from '$lib/server/db/queries'; 5 | 6 | export const GET: RequestHandler = async ({ locals }) => { 7 | if (!locals.user) redirect(302, '/login'); 8 | 9 | try { 10 | const userId = locals.user.id; 11 | const savesWithGroups = await getSavesWithGroups(userId); 12 | 13 | if (savesWithGroups.length === 0) { 14 | return json(savesWithGroups) 15 | } 16 | 17 | const groupCounts: Record = {}; 18 | let noGroupCount = 0; 19 | 20 | savesWithGroups.forEach(item => { 21 | if (item.saveGroups.length === 0) { 22 | noGroupCount++; 23 | } else { 24 | item.saveGroups.forEach(saveGroup => { 25 | const groupId = saveGroup.groupId; 26 | // Increment count for saves with groups 27 | groupCounts[groupId] = (groupCounts[groupId] || 0) + 1; 28 | }); 29 | } 30 | }); 31 | 32 | return json({ 33 | saves: savesWithGroups, 34 | groupCounts: groupCounts, 35 | noGroupCount: noGroupCount 36 | }); 37 | 38 | } catch (err) { 39 | console.log('error fetch saves', err); 40 | return error(400, { 41 | message: 'Failed to fetch saves' 42 | }); 43 | } 44 | }; 45 | -------------------------------------------------------------------------------- /packages/ui/components/ui/select/select-content.svelte: -------------------------------------------------------------------------------- 1 | 22 | 23 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /apps/web/src/lib/components/app/saves/item/types/item-website.svelte: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | {#if $liveView} 21 | 22 | 23 | 24 | {:else} 25 | 26 | {/if} 27 | 28 | 29 | 30 | 31 | {cleanUrl(url)} 32 | 33 | -------------------------------------------------------------------------------- /packages/ui/components/ui/scroll-area/scroll-area.svelte: -------------------------------------------------------------------------------- 1 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | {#if orientation === "vertical" || orientation === "both"} 26 | 27 | {/if} 28 | {#if orientation === "horizontal" || orientation === "both"} 29 | 30 | {/if} 31 | 32 | 33 | -------------------------------------------------------------------------------- /packages/ui/components/ui/slider/slider.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 18 | 19 | 20 | 21 | {#each thumbs as thumb} 22 | 26 | {/each} 27 | 28 | -------------------------------------------------------------------------------- /packages/ui/components/ui/alert-dialog/index.ts: -------------------------------------------------------------------------------- 1 | import { AlertDialog as AlertDialogPrimitive } from "bits-ui"; 2 | 3 | // const Root = AlertDialogPrimitive.Root; 4 | const Trigger = AlertDialogPrimitive.Trigger; 5 | 6 | import Root from './alert-dialog.svelte'; 7 | import Title from "./alert-dialog-title.svelte"; 8 | import Action from "./alert-dialog-action.svelte"; 9 | import Cancel from "./alert-dialog-cancel.svelte"; 10 | import Portal from "./alert-dialog-portal.svelte"; 11 | import Footer from "./alert-dialog-footer.svelte"; 12 | import Header from "./alert-dialog-header.svelte"; 13 | import Overlay from "./alert-dialog-overlay.svelte"; 14 | import Content from "./alert-dialog-content.svelte"; 15 | import Description from "./alert-dialog-description.svelte"; 16 | 17 | export { 18 | Root, 19 | Title, 20 | Action, 21 | Cancel, 22 | Portal, 23 | Footer, 24 | Header, 25 | Trigger, 26 | Overlay, 27 | Content, 28 | Description, 29 | // 30 | Root as AlertDialog, 31 | Title as AlertDialogTitle, 32 | Action as AlertDialogAction, 33 | Cancel as AlertDialogCancel, 34 | Portal as AlertDialogPortal, 35 | Footer as AlertDialogFooter, 36 | Header as AlertDialogHeader, 37 | Trigger as AlertDialogTrigger, 38 | Overlay as AlertDialogOverlay, 39 | Content as AlertDialogContent, 40 | Description as AlertDialogDescription, 41 | }; 42 | -------------------------------------------------------------------------------- /packages/ui/components/ui/command/command-dialog.svelte: -------------------------------------------------------------------------------- 1 | 20 | 21 | 22 | 23 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /packages/ui/components/ui/switch/switch.svelte: -------------------------------------------------------------------------------- 1 | 11 | 12 | 24 | 31 | 32 | -------------------------------------------------------------------------------- /packages/ui/components/ui/checkbox/checkbox.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 23 | 28 | {#if isChecked} 29 | 30 | {:else if isIndeterminate} 31 | 32 | {/if} 33 | 34 | 35 | -------------------------------------------------------------------------------- /packages/ui/components/ui/context-menu/context-menu-sub-trigger.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /packages/ui/components/ui/dropdown-menu/dropdown-menu-sub-trigger.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /apps/web/src/lib/components/app/saves/item/item-image.svelte: -------------------------------------------------------------------------------- 1 | 24 | 25 | {#if imageUrl} 26 | {#if absolute} 27 | 28 | {/if} 29 | imageUrl = ''} 36 | /> 37 | {:else} 38 | 39 | 40 | 41 | {/if} -------------------------------------------------------------------------------- /apps/web/src/routes/(auth)/login/user-login-form.svelte: -------------------------------------------------------------------------------- 1 | 22 | 23 | 24 | { 25 | loading.github = true 26 | goto('/login/github'); 27 | }}> 28 | {#if loading.github} 29 | 30 | {:else} 31 | 32 | {/if} 33 | {' '} 34 | GitHub 35 | 36 | 37 | { 38 | loading.google = true 39 | goto('/login/google'); 40 | }}> 41 | {#if loading.google} 42 | 43 | {:else} 44 | 45 | {/if} 46 | {' '} 47 | Google 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /packages/ui/components/ui/dropdown-menu/dropdown-menu-radio-item.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /packages/ui/components/ui/context-menu/context-menu-checkbox-item.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /packages/ui/components/ui/context-menu/context-menu-radio-item.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /apps/web/src/lib/components/site/hero/context.ts: -------------------------------------------------------------------------------- 1 | import { getContext, setContext } from 'svelte'; 2 | import { animate, motionValue, useMotionTemplate } from 'svelte-motion'; 3 | import { get, writable } from 'svelte/store'; 4 | 5 | let widthPercentageNumber = motionValue(0); 6 | let widthPercentage = useMotionTemplate`${widthPercentageNumber}%`; 7 | let isMouseOver = writable(false); 8 | let manuallyAnimating = writable(false); 9 | 10 | function animateShimmer() { 11 | manuallyAnimating.set(true); 12 | let start; 13 | let end; 14 | 15 | if (widthPercentageNumber.get() > 30) { 16 | start = widthPercentageNumber.get(); 17 | end = -50; 18 | } else { 19 | start = widthPercentageNumber.get(); 20 | end = 120; 21 | } 22 | isMouseOver.set(true); 23 | widthPercentageNumber.set(start); 24 | animate(widthPercentageNumber, end, { duration: 0.5, onComplete: () => { 25 | isMouseOver.set(false); 26 | manuallyAnimating.set(false); 27 | }}); 28 | } 29 | 30 | const HERO_KEY = Symbol('Hero'); 31 | 32 | export function setHeroContext() { 33 | return setContext(HERO_KEY, { 34 | widthPercentage, 35 | widthPercentageNumber, 36 | isMouseOver, 37 | manuallyAnimating, 38 | animateShimmer, 39 | }); 40 | } 41 | 42 | export function getHeroContext() { 43 | return getContext>(HERO_KEY); 44 | } -------------------------------------------------------------------------------- /packages/ui/components/ui/dialog/dialog-content.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 | 19 | 28 | 29 | 32 | 33 | Close 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /packages/ui/components/ui/dropdown-menu/dropdown-menu-checkbox-item.svelte: -------------------------------------------------------------------------------- 1 | 13 | 14 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /apps/web/src/lib/components/app/copy-button.svelte: -------------------------------------------------------------------------------- 1 | 28 | 29 | 30 | 31 | handleUrlCopy(copyString)}> 32 | 33 | 34 | 35 | 36 | {currentCopyText} 37 | 38 | -------------------------------------------------------------------------------- /apps/web/src/lib/components/site/logos/colors.svelte: -------------------------------------------------------------------------------- 1 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /packages/ui/components/ui/dropdown-menu/dropdown-menu-link.svelte: -------------------------------------------------------------------------------- 1 | 16 | 17 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /packages/ui/components/ui/select/select-item.svelte: -------------------------------------------------------------------------------- 1 | 15 | 16 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /apps/web/src/routes/api/saves/metadata/update/+server.ts: -------------------------------------------------------------------------------- 1 | import type { RequestHandler } from './$types'; 2 | 3 | import { db } from '$lib/server/db'; 4 | import { save } from '$lib/server/db/schema'; 5 | import { error, json, redirect } from '@sveltejs/kit'; 6 | import { eq } from 'drizzle-orm'; 7 | 8 | export const POST: RequestHandler = async ({ request, locals, fetch }) => { 9 | if (!locals.user) redirect(302, '/login'); 10 | 11 | try { 12 | const data = await request.json(); 13 | const saveId = data['id']; 14 | 15 | if (!saveId) { 16 | return error(400, { 17 | message: 'Invalid save id' 18 | }); 19 | } 20 | 21 | const saveItem = await db.query.save.findFirst({ 22 | where: (save, { eq }) => eq(save.id, saveId), 23 | }); 24 | 25 | const metaDataResponse = await fetch('/api/saves/metadata', { 26 | method: 'POST', 27 | headers: { 28 | 'Content-Type': 'application/json' 29 | }, 30 | body: JSON.stringify({ url: saveItem?.url }) 31 | }); 32 | const { title, description, imageUrl, faviconUrl } = await metaDataResponse.json(); 33 | 34 | await db.update(save) 35 | .set({ 36 | title, 37 | description, 38 | imageUrl, 39 | faviconUrl 40 | }).where(eq(save.id, saveId)); 41 | 42 | return json({ success: true }); 43 | 44 | } catch (err) { 45 | console.log('error update save metadata', err); 46 | return error(400, { 47 | message: 'Failed to update save metadata' 48 | }); 49 | } 50 | 51 | }; -------------------------------------------------------------------------------- /apps/launcher/static/vite.svg: -------------------------------------------------------------------------------- 1 | --------------------------------------------------------------------------------
12 | 13 |
{message}
404 Error
Your digital vault awaits – let's get started!
23 | By logging in, you agree to our{' '} 24 | 25 | Privacy Policy 26 | 27 | . 28 |