Compare commits
14 Commits
462866b8f2
...
v0.0.8
| Author | SHA1 | Date | |
|---|---|---|---|
| 8105f57be9 | |||
| 15565b7208 | |||
| d1c46cdee1 | |||
| d33b9c5467 | |||
| df0e78d9ba | |||
| cc596e632f | |||
|
|
7923514c5c | ||
|
|
7cd943cced | ||
|
|
0016e2104c | ||
|
|
752ca147ff | ||
|
|
56b57aa8de | ||
|
|
ae823b755e | ||
|
|
73dc856821 | ||
|
|
e35a038f09 |
39
.gitea/workflows/publish.yml
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
name: Publish npm package
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
tags:
|
||||||
|
- "v*"
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
publish:
|
||||||
|
runs-on: x86
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- uses: oven-sh/setup-bun@v2
|
||||||
|
with:
|
||||||
|
bun-version: latest
|
||||||
|
|
||||||
|
- uses: actions/setup-node@v4
|
||||||
|
with:
|
||||||
|
node-version: 24
|
||||||
|
registry-url: "https://registry.npmjs.org"
|
||||||
|
|
||||||
|
- name: Install dependencies
|
||||||
|
run: bun install --frozen-lockfile
|
||||||
|
|
||||||
|
- name: Check tag matches package version
|
||||||
|
run: |
|
||||||
|
PACKAGE_VERSION="v$(node -p "require('./package.json').version")"
|
||||||
|
TAG_NAME="${GITHUB_REF_NAME:-${GITHUB_REF#refs/tags/}}"
|
||||||
|
test "$PACKAGE_VERSION" = "$TAG_NAME"
|
||||||
|
|
||||||
|
- name: Build
|
||||||
|
run: bun run build
|
||||||
|
|
||||||
|
- name: Publish
|
||||||
|
run: npm publish --access public
|
||||||
|
env:
|
||||||
|
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
|
||||||
15
README.md
@@ -1,8 +1,11 @@
|
|||||||
# create-glstack
|
# create-glstack
|
||||||
|
|
||||||
Scaffold a fullstack application with a Go backend, SvelteKit frontend, and PostgreSQL database, connected via Connect RPC.
|
Scaffold a fullstack application with a Go backend, PostgreSQL database, and your choice of frontend framework, connected via Connect RPC.
|
||||||
|
|
||||||
> **Work in progress** -- additional frontend frameworks and features are planned for future releases.
|
Supported frontends:
|
||||||
|
|
||||||
|
- [SvelteKit](https://kit.svelte.dev/)
|
||||||
|
- [Solid Start](https://start.solidjs.com/)
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -15,7 +18,7 @@ bun create glstack
|
|||||||
A monorepo with the following structure:
|
A monorepo with the following structure:
|
||||||
|
|
||||||
- **`services/api`** -- Go backend using [Cobra](https://github.com/spf13/cobra) for CLI, [pgx](https://github.com/jackc/pgx) for Postgres, and [Connect RPC](https://connectrpc.com/) for the API layer
|
- **`services/api`** -- Go backend using [Cobra](https://github.com/spf13/cobra) for CLI, [pgx](https://github.com/jackc/pgx) for Postgres, and [Connect RPC](https://connectrpc.com/) for the API layer
|
||||||
- **`apps/web`** -- SvelteKit frontend with [shadcn-svelte](https://shadcn-svelte.com/), TailwindCSS, [TanStack Query](https://tanstack.com/query), and [Paraglide](https://inlang.com/m/gerre34r/library-inlang-paraglideJs) for i18n
|
- **`apps/web`** -- Frontend app (SvelteKit or Solid Start) with TailwindCSS and [TanStack Query](https://tanstack.com/query)
|
||||||
- **`packages/proto`** -- Protobuf service definitions with [Buf](https://buf.build/) for codegen (Go + TypeScript)
|
- **`packages/proto`** -- Protobuf service definitions with [Buf](https://buf.build/) for codegen (Go + TypeScript)
|
||||||
- **`packages/rpc`** -- Generated TypeScript Connect RPC client shared across frontend apps
|
- **`packages/rpc`** -- Generated TypeScript Connect RPC client shared across frontend apps
|
||||||
|
|
||||||
@@ -23,16 +26,16 @@ A monorepo with the following structure:
|
|||||||
|
|
||||||
The template includes a [devenv.nix](https://devenv.sh/) configuration that sets up:
|
The template includes a [devenv.nix](https://devenv.sh/) configuration that sets up:
|
||||||
|
|
||||||
- Go, TypeScript, Bun
|
- Go, TypeScript, Bun, Node.js
|
||||||
- PostgreSQL with an auto-provisioned database
|
- PostgreSQL with an auto-provisioned database
|
||||||
- File watchers for protobuf, SQL, and frontend hot reload
|
- File watchers for protobuf, SQL, and frontend hot reload with proper process dependency ordering
|
||||||
- [Air](https://github.com/air-verse/air) for Go live reload
|
- [Air](https://github.com/air-verse/air) for Go live reload
|
||||||
- [sqlc](https://sqlc.dev/) for type-safe SQL
|
- [sqlc](https://sqlc.dev/) for type-safe SQL
|
||||||
- [dbmate](https://github.com/amacneil/dbmate) for database migrations
|
- [dbmate](https://github.com/amacneil/dbmate) for database migrations
|
||||||
|
|
||||||
### Starter example
|
### Starter example
|
||||||
|
|
||||||
The generated project includes a working Todo CRUD example wired end-to-end: protobuf schema, Go service implementation, SQL queries, and a SvelteKit UI.
|
The generated project includes a working Todo CRUD example wired end-to-end: protobuf schema, Go service implementation, SQL queries, and frontend UI.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
|
|||||||
4
bun.lock
@@ -6,7 +6,7 @@
|
|||||||
"name": "glstack",
|
"name": "glstack",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@clack/prompts": "^1.2.0",
|
"@clack/prompts": "^1.2.0",
|
||||||
"@gregorlohaus/tdir": "^0.1.1",
|
"@gregorlohaus/tdir": "^0.2.0",
|
||||||
"zod": "^4.3.6",
|
"zod": "^4.3.6",
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
|
|
||||||
"@clack/prompts": ["@clack/prompts@1.2.0", "", { "dependencies": { "@clack/core": "1.2.0", "fast-string-width": "^1.1.0", "fast-wrap-ansi": "^0.1.3", "sisteransi": "^1.0.5" } }, "sha512-4jmztR9fMqPMjz6H/UZXj0zEmE43ha1euENwkckKKel4XpSfokExPo5AiVStdHSAlHekz4d0CA/r45Ok1E4D3w=="],
|
"@clack/prompts": ["@clack/prompts@1.2.0", "", { "dependencies": { "@clack/core": "1.2.0", "fast-string-width": "^1.1.0", "fast-wrap-ansi": "^0.1.3", "sisteransi": "^1.0.5" } }, "sha512-4jmztR9fMqPMjz6H/UZXj0zEmE43ha1euENwkckKKel4XpSfokExPo5AiVStdHSAlHekz4d0CA/r45Ok1E4D3w=="],
|
||||||
|
|
||||||
"@gregorlohaus/tdir": ["@gregorlohaus/tdir@0.1.1", "", { "peerDependencies": { "zod": "^4" } }, "sha512-4NlHif5Pn6Vh1TzCj8B1d+pz8ab5/CodC2Cq9HVr1wHdFlgXM/yjtZEDLdBMtwqz1n1oCCEjzV7XYbin2ywsjQ=="],
|
"@gregorlohaus/tdir": ["@gregorlohaus/tdir@0.2.0", "", { "peerDependencies": { "zod": "^4" }, "bin": { "tdir": "dist/cli.js" } }, "sha512-LwISQC7iARezu7SQZJk9TV8Sqp8I/fVMssH18lWL1KD/ETpWp2LwwTzm/j9aWcs+Eo5OBQctbqFTafo9Nfue+A=="],
|
||||||
|
|
||||||
"@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
|
"@types/bun": ["@types/bun@1.3.11", "", { "dependencies": { "bun-types": "1.3.11" } }, "sha512-5vPne5QvtpjGpsGYXiFyycfpDF2ECyPcTSsFBMa0fraoxiQyMJ3SmuQIGhzPg2WJuWxVBoxWJ2kClYTcw/4fAg=="],
|
||||||
|
|
||||||
|
|||||||
39
index.ts
@@ -5,7 +5,7 @@ import { initRenderer } from '@gregorlohaus/tdir'
|
|||||||
import { z } from 'zod'
|
import { z } from 'zod'
|
||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
|
||||||
p.intro("create-glstack");
|
p.intro(`create-glstack ${process.env.GLSTACK_DEV && 'isDev'}`);
|
||||||
|
|
||||||
const project = await p.group(
|
const project = await p.group(
|
||||||
{
|
{
|
||||||
@@ -18,6 +18,25 @@ const project = await p.group(
|
|||||||
return undefined
|
return undefined
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
frontend: async () =>
|
||||||
|
await p.select({
|
||||||
|
message: 'Pick a frontend framework.',
|
||||||
|
options: [
|
||||||
|
{ value: "svelte-kit", label: "SvelteKit" },
|
||||||
|
{ value: "solid-start", label: "SolidStart" },
|
||||||
|
{ value: "none", label: "None" }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
,
|
||||||
|
mobile: async () =>
|
||||||
|
await p.select({
|
||||||
|
message: 'Pick a mobile framework.',
|
||||||
|
options: [
|
||||||
|
{ value: "expo", label: "ReactNative + Expo" },
|
||||||
|
{ value: "none", label: "None" }
|
||||||
|
]
|
||||||
|
})
|
||||||
|
,
|
||||||
goprefix: () =>
|
goprefix: () =>
|
||||||
p.text({
|
p.text({
|
||||||
message: "What would you like to use as a go package prefix?",
|
message: "What would you like to use as a go package prefix?",
|
||||||
@@ -27,6 +46,9 @@ const project = await p.group(
|
|||||||
return undefined
|
return undefined
|
||||||
},
|
},
|
||||||
}),
|
}),
|
||||||
|
installDeps: async () => {
|
||||||
|
return await p.confirm({message: "Install dependencies?", })
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
onCancel: () => {
|
onCancel: () => {
|
||||||
@@ -35,22 +57,25 @@ const project = await p.group(
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
const createRenderer = initRenderer(path.join(import.meta.dir, '..', 'template'))
|
|
||||||
|
const templateDir = (process.env.GLSTACK_DEV == 'true' ? './template' : path.join(import.meta.dir, '..', 'template'))
|
||||||
|
const createRenderer = initRenderer(templateDir)
|
||||||
const render = createRenderer(z.object({
|
const render = createRenderer(z.object({
|
||||||
project: z.object({
|
project: z.object({
|
||||||
name: z.string(),
|
name: z.string(),
|
||||||
goprefix: z.string()
|
goprefix: z.string(),
|
||||||
|
frontend: z.string(),
|
||||||
|
mobile: z.string()
|
||||||
})
|
})
|
||||||
}))
|
}))
|
||||||
const destDir = path.join("./", project.name);
|
const destDir = path.join("./", project.name);
|
||||||
render(destDir,{project})
|
render(destDir, { project }, { reverseMap: true })
|
||||||
// TODO: template rendering — copy/generate files into destDir
|
|
||||||
|
|
||||||
const s = p.spinner();
|
const s = p.spinner();
|
||||||
|
if (project.installDeps) {
|
||||||
s.start("Installing dependencies");
|
s.start("Installing dependencies");
|
||||||
await Bun.$`bun install`.cwd(path.join(destDir)).quiet();
|
await Bun.$`bun install`.cwd(path.join(destDir)).quiet();
|
||||||
await Bun.$`bun install`.cwd(path.join(destDir,'packages','rpc')).quiet();
|
|
||||||
await Bun.$`bun install`.cwd(path.join(destDir,'apps','web')).quiet();
|
|
||||||
s.stop("Dependencies installed.");
|
s.stop("Dependencies installed.");
|
||||||
|
}
|
||||||
|
|
||||||
p.outro("You're all set!");
|
p.outro("You're all set!");
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "create-glstack",
|
"name": "create-glstack",
|
||||||
"version": "0.0.3",
|
"version": "0.0.8",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -22,7 +22,7 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@clack/prompts": "^1.2.0",
|
"@clack/prompts": "^1.2.0",
|
||||||
"@gregorlohaus/tdir": "^0.1.1",
|
"@gregorlohaus/tdir": "^0.2.0",
|
||||||
"zod": "^4.3.6"
|
"zod": "^4.3.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
28
template/apps/<@if(eq(context.project.frontend,"solid-start"))>web/.gitignore
vendored
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
dist
|
||||||
|
.wrangler
|
||||||
|
.output
|
||||||
|
.vercel
|
||||||
|
.netlify
|
||||||
|
.vinxi
|
||||||
|
app.config.timestamp_*.js
|
||||||
|
|
||||||
|
# Environment
|
||||||
|
.env
|
||||||
|
.env*.local
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
/node_modules
|
||||||
|
|
||||||
|
# IDEs and editors
|
||||||
|
/.idea
|
||||||
|
.project
|
||||||
|
.classpath
|
||||||
|
*.launch
|
||||||
|
.settings/
|
||||||
|
|
||||||
|
# Temp
|
||||||
|
gitignore
|
||||||
|
|
||||||
|
# System Files
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
{
|
||||||
|
"name": "example-bare",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite dev",
|
||||||
|
"build": "vite build",
|
||||||
|
"start": "vite start",
|
||||||
|
"preview": "vite preview"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@<@var(context.project.name)>/rpc": "workspace:*",
|
||||||
|
"@solidjs/start": "2.0.0-alpha.2",
|
||||||
|
"@solidjs/vite-plugin-nitro-2": "^0.1.0",
|
||||||
|
"solid-js": "^1.9.5",
|
||||||
|
"vite": "^7.0.0"
|
||||||
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">=22"
|
||||||
|
},
|
||||||
|
"devDependencies": {}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 664 B |
@@ -0,0 +1,61 @@
|
|||||||
|
body {
|
||||||
|
font-family: Gordita, Roboto, Oxygen, Ubuntu, Cantarell, "Open Sans", "Helvetica Neue", sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
margin-right: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
main {
|
||||||
|
text-align: center;
|
||||||
|
padding: 1em;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
color: #335d92;
|
||||||
|
text-transform: uppercase;
|
||||||
|
font-size: 4rem;
|
||||||
|
font-weight: 100;
|
||||||
|
line-height: 1.1;
|
||||||
|
margin: 4rem auto;
|
||||||
|
max-width: 14rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
max-width: 14rem;
|
||||||
|
margin: 2rem auto;
|
||||||
|
line-height: 1.35;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 480px) {
|
||||||
|
h1 {
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
max-width: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.increment {
|
||||||
|
font-family: inherit;
|
||||||
|
font-size: inherit;
|
||||||
|
padding: 1em 2em;
|
||||||
|
color: #335d92;
|
||||||
|
background-color: rgba(68, 107, 158, 0.1);
|
||||||
|
border-radius: 2em;
|
||||||
|
border: 2px solid rgba(68, 107, 158, 0);
|
||||||
|
outline: none;
|
||||||
|
width: 200px;
|
||||||
|
font-variant-numeric: tabular-nums;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.increment:focus {
|
||||||
|
border: 2px solid #335d92;
|
||||||
|
}
|
||||||
|
|
||||||
|
.increment:active {
|
||||||
|
background-color: rgba(68, 107, 158, 0.2);
|
||||||
|
}
|
||||||
@@ -0,0 +1,58 @@
|
|||||||
|
import { createSignal, For } from "solid-js";
|
||||||
|
import { createRouter, Todo } from "@<@var(context.project.name)>/rpc"
|
||||||
|
import { createEffect } from 'solid-js'
|
||||||
|
import "./app.css";
|
||||||
|
|
||||||
|
export default function App() {
|
||||||
|
const router = createRouter("http://127.0.0.1:8080")
|
||||||
|
const [todos, setTodos] = createSignal<Todo[]>([]);
|
||||||
|
const [todoToCreateTask, setTodoToCreateTask] = createSignal<string>("");
|
||||||
|
const fetchTodos = () => {
|
||||||
|
router.todos.listTodos({}).then((r) => {
|
||||||
|
setTodos(r.todos)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
createEffect(() => {
|
||||||
|
fetchTodos()
|
||||||
|
})
|
||||||
|
|
||||||
|
const setTodoDone = (id: string, task: string) => {
|
||||||
|
return async (event : Event & { currentTarget: HTMLInputElement }) => {
|
||||||
|
await router.todos.updateTodo({ todo: { id, task, done: event.currentTarget.checked } })
|
||||||
|
fetchTodos()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteTodo = (id: string) => {
|
||||||
|
return async () => {
|
||||||
|
await router.todos.deleteTodo({ todo: { id } })
|
||||||
|
fetchTodos()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createTodo = () => {
|
||||||
|
router.todos.createTodo({ todo: { id: crypto.randomUUID(), task: todoToCreateTask() } }).then((r) => {
|
||||||
|
console.log(r)
|
||||||
|
fetchTodos()
|
||||||
|
}).catch((e) => {
|
||||||
|
console.log(e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<p>Create Todo</p>
|
||||||
|
<input value={todoToCreateTask()} onInput={(e) => setTodoToCreateTask(e.currentTarget.value)} type="text"/>
|
||||||
|
<button onclick={createTodo}> create </button>
|
||||||
|
<For each={todos()}>
|
||||||
|
{(item:Todo) =>
|
||||||
|
<div>
|
||||||
|
{item.task}
|
||||||
|
<input type='checkbox' checked={item.done} onChange={setTodoDone(item.id || "",item.task)}/>
|
||||||
|
<button onclick={deleteTodo(item.id || "")}>delete</button>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</For>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
// @refresh reload
|
||||||
|
import { mount, StartClient } from "@solidjs/start/client";
|
||||||
|
|
||||||
|
mount(() => <StartClient />, document.getElementById("app")!);
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
// @refresh reload
|
||||||
|
import { createHandler, StartServer } from "@solidjs/start/server";
|
||||||
|
|
||||||
|
export default createHandler(() => (
|
||||||
|
<StartServer
|
||||||
|
document={({ assets, children, scripts }) => (
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<link rel="icon" href="/favicon.ico" />
|
||||||
|
{assets}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div id="app">{children}</div>
|
||||||
|
{scripts}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
));
|
||||||
1
template/apps/<@if(eq(context.project.frontend,"solid-start"))>web/src/global.d.ts
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
/// <reference types="@solidjs/start/env" />
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "ESNext",
|
||||||
|
"module": "ESNext",
|
||||||
|
"moduleResolution": "bundler",
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"jsx": "preserve",
|
||||||
|
"jsxImportSource": "solid-js",
|
||||||
|
"allowJs": true,
|
||||||
|
"strict": true,
|
||||||
|
"noEmit": true,
|
||||||
|
"types": ["vite/client"],
|
||||||
|
"isolatedModules": true,
|
||||||
|
"paths": {
|
||||||
|
"~/*": ["./src/*"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { defineConfig } from "vite";
|
||||||
|
import { nitroV2Plugin as nitro } from "@solidjs/vite-plugin-nitro-2";
|
||||||
|
|
||||||
|
import { solidStart } from "@solidjs/start/config";
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [solidStart(),
|
||||||
|
nitro()
|
||||||
|
]
|
||||||
|
});
|
||||||
@@ -21,6 +21,3 @@ Thumbs.db
|
|||||||
# Vite
|
# Vite
|
||||||
vite.config.js.timestamp-*
|
vite.config.js.timestamp-*
|
||||||
vite.config.ts.timestamp-*
|
vite.config.ts.timestamp-*
|
||||||
# Paraglide
|
|
||||||
src/lib/paraglide
|
|
||||||
project.inlang/cache/
|
|
||||||
3
template/apps/<@if(eq(context.project.frontend,"svelte-kit"))>web/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"recommendations": ["svelte.svelte-vscode"]
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"name": "web",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.1",
|
||||||
|
"type": "module",
|
||||||
|
"scripts": {
|
||||||
|
"dev": "vite dev",
|
||||||
|
"build": "vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"prepare": "svelte-kit sync || echo ''",
|
||||||
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@sveltejs/adapter-auto": "^7.0.1",
|
||||||
|
"@sveltejs/kit": "^2.57.0",
|
||||||
|
"@sveltejs/vite-plugin-svelte": "^7.0.0",
|
||||||
|
"svelte": "^5.55.2",
|
||||||
|
"svelte-check": "^4.4.6",
|
||||||
|
"typescript": "^6.0.2",
|
||||||
|
"vite": "^8.0.7"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@<@var(context.project.name)>/rpc": "workspace:*"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,15 +1,11 @@
|
|||||||
<!doctype html>
|
<!doctype html>
|
||||||
|
<html lang="en">
|
||||||
<html lang="%paraglide.lang%" dir="%paraglide.dir%">
|
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
|
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
|
||||||
<meta name="text-scale" content="scale" />
|
<meta name="text-scale" content="scale" />
|
||||||
%sveltekit.head%
|
%sveltekit.head%
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body data-sveltekit-preload-data="hover">
|
<body data-sveltekit-preload-data="hover">
|
||||||
<div style="display: contents">%sveltekit.body%</div>
|
<div style="display: contents">%sveltekit.body%</div>
|
||||||
</body>
|
</body>
|
||||||
|
Before Width: | Height: | Size: 1.5 KiB After Width: | Height: | Size: 1.5 KiB |
@@ -0,0 +1,11 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import favicon from '$lib/assets/favicon.svg';
|
||||||
|
|
||||||
|
let { children } = $props();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<link rel="icon" href={favicon} />
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
{@render children()}
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { createRouter, type Todo } from "@glstack-test/rpc"
|
||||||
|
import type { ChangeEventHandler } from "svelte/elements";
|
||||||
|
const router = createRouter("http://127.0.0.1:8080");
|
||||||
|
let todos = $state<Todo[]>([])
|
||||||
|
let todoToCreateTask = $state<string>("")
|
||||||
|
const fetchTodos = () => {
|
||||||
|
router.todos.listTodos({}).then((r) => {
|
||||||
|
todos = r.todos;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
$effect(() => {
|
||||||
|
fetchTodos()
|
||||||
|
})
|
||||||
|
|
||||||
|
const setTodoDone = (id: string, task: string) => {
|
||||||
|
return async (event : Event & { currentTarget: HTMLInputElement }) => {
|
||||||
|
await router.todos.updateTodo({ todo: { id, task, done: event.currentTarget.checked } })
|
||||||
|
fetchTodos()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteTodo = (id: string) => {
|
||||||
|
return async () => {
|
||||||
|
await router.todos.deleteTodo({ todo: { id } })
|
||||||
|
fetchTodos()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createTodo = () => {
|
||||||
|
router.todos.createTodo({ todo: { id: crypto.randomUUID(), task: todoToCreateTask } }).then((r) => {
|
||||||
|
console.log(r)
|
||||||
|
fetchTodos()
|
||||||
|
}).catch((e) => {
|
||||||
|
console.log(e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<h1>Create Todo</h1>
|
||||||
|
<input bind:value={todoToCreateTask} type="text"/>
|
||||||
|
<button onclick={createTodo}> create </button>
|
||||||
|
{#each todos as todo (todo.id)}
|
||||||
|
<div>
|
||||||
|
{todo.task}
|
||||||
|
<input type='checkbox' onchange={setTodoDone(todo.id || "",todo.task)}/>
|
||||||
|
<button onclick={deleteTodo(todo.id || "")}>delete</button>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
@@ -1,17 +1,10 @@
|
|||||||
import adapter from '@sveltejs/adapter-auto';
|
import adapter from '@sveltejs/adapter-auto';
|
||||||
import { relative, sep } from 'node:path';
|
|
||||||
|
|
||||||
/** @type {import('@sveltejs/kit').Config} */
|
/** @type {import('@sveltejs/kit').Config} */
|
||||||
const config = {
|
const config = {
|
||||||
compilerOptions: {
|
compilerOptions: {
|
||||||
// defaults to rune mode for the project, except for `node_modules`. Can be removed in svelte 6.
|
// Force runes mode for the project, except for libraries. Can be removed in svelte 6.
|
||||||
runes: ({ filename }) => {
|
runes: ({ filename }) => (filename.split(/[/\\]/).includes('node_modules') ? undefined : true)
|
||||||
const relativePath = relative(import.meta.dirname, filename);
|
|
||||||
const pathSegments = relativePath.toLowerCase().split(sep);
|
|
||||||
const isExternalLibrary = pathSegments.includes('node_modules');
|
|
||||||
|
|
||||||
return isExternalLibrary ? undefined : true;
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
kit: {
|
kit: {
|
||||||
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
|
// adapter-auto only supports some environments, see https://svelte.dev/docs/kit/adapter-auto for a list.
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
import { sveltekit } from '@sveltejs/kit/vite';
|
||||||
|
import { defineConfig } from 'vite';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
plugins: [sveltekit()]
|
||||||
|
});
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"enabledPlugins": {
|
||||||
|
"expo@claude-plugins-official": true
|
||||||
|
}
|
||||||
|
}
|
||||||
43
template/apps/<@if(eq(context.project.mobile,"expo"))>mobile/.gitignore
vendored
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
# Learn more https://docs.github.com/en/get-started/getting-started-with-git/ignoring-files
|
||||||
|
|
||||||
|
# dependencies
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Expo
|
||||||
|
.expo/
|
||||||
|
dist/
|
||||||
|
web-build/
|
||||||
|
expo-env.d.ts
|
||||||
|
|
||||||
|
# Native
|
||||||
|
.kotlin/
|
||||||
|
*.orig.*
|
||||||
|
*.jks
|
||||||
|
*.p8
|
||||||
|
*.p12
|
||||||
|
*.key
|
||||||
|
*.mobileprovision
|
||||||
|
|
||||||
|
# Metro
|
||||||
|
.metro-health-check*
|
||||||
|
|
||||||
|
# debug
|
||||||
|
npm-debug.*
|
||||||
|
yarn-debug.*
|
||||||
|
yarn-error.*
|
||||||
|
|
||||||
|
# macOS
|
||||||
|
.DS_Store
|
||||||
|
*.pem
|
||||||
|
|
||||||
|
# local env files
|
||||||
|
.env*.local
|
||||||
|
|
||||||
|
# typescript
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
example
|
||||||
|
|
||||||
|
# generated native folders
|
||||||
|
/ios
|
||||||
|
/android
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
.expo
|
||||||
|
android
|
||||||
|
node_modules
|
||||||
|
assets
|
||||||
1
template/apps/<@if(eq(context.project.mobile,"expo"))>mobile/.vscode/extensions.json
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{ "recommendations": ["expo.vscode-expo-tools"] }
|
||||||
7
template/apps/<@if(eq(context.project.mobile,"expo"))>mobile/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"editor.codeActionsOnSave": {
|
||||||
|
"source.fixAll": "explicit",
|
||||||
|
"source.organizeImports": "explicit",
|
||||||
|
"source.sortMembers": "explicit"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# Expo HAS CHANGED
|
||||||
|
|
||||||
|
Read the exact versioned docs at https://docs.expo.dev/versions/v56.0.0/ before writing any code.
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
@AGENTS.md
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
The MIT License (MIT)
|
||||||
|
|
||||||
|
Copyright (c) 2015-present 650 Industries, Inc. (aka Expo)
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
@@ -0,0 +1,45 @@
|
|||||||
|
{
|
||||||
|
"expo": {
|
||||||
|
"name": "mobile",
|
||||||
|
"slug": "mobile",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"orientation": "portrait",
|
||||||
|
"icon": "./assets/images/icon.png",
|
||||||
|
"scheme": "mobile",
|
||||||
|
"userInterfaceStyle": "automatic",
|
||||||
|
"ios": {
|
||||||
|
"icon": "./assets/expo.icon"
|
||||||
|
},
|
||||||
|
"android": {
|
||||||
|
"adaptiveIcon": {
|
||||||
|
"backgroundColor": "#E6F4FE",
|
||||||
|
"foregroundImage": "./assets/images/android-icon-foreground.png",
|
||||||
|
"backgroundImage": "./assets/images/android-icon-background.png",
|
||||||
|
"monochromeImage": "./assets/images/android-icon-monochrome.png"
|
||||||
|
},
|
||||||
|
"predictiveBackGestureEnabled": false,
|
||||||
|
"package": "com.gregorl.mobile"
|
||||||
|
},
|
||||||
|
"web": {
|
||||||
|
"output": "static",
|
||||||
|
"favicon": "./assets/images/favicon.png"
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"expo-router",
|
||||||
|
[
|
||||||
|
"expo-splash-screen",
|
||||||
|
{
|
||||||
|
"backgroundColor": "#208AEF",
|
||||||
|
"android": {
|
||||||
|
"image": "./assets/images/splash-icon.png",
|
||||||
|
"imageWidth": 76
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
],
|
||||||
|
"experiments": {
|
||||||
|
"typedRoutes": true,
|
||||||
|
"reactCompiler": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
<svg width="652" height="606" viewBox="0 0 652 606" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M353.554 0H298.446C273.006 0 249.684 14.6347 237.962 37.9539L4.37994 502.646C-1.04325 513.435 -1.45067 526.178 3.2716 537.313L22.6123 582.918C34.6475 611.297 72.5404 614.156 88.4414 587.885L309.863 222.063C313.34 216.317 319.439 212.826 326 212.826C332.561 212.826 338.659 216.317 342.137 222.063L563.559 587.885C579.46 614.156 617.352 611.297 629.388 582.918L648.728 537.313C653.451 526.178 653.043 513.435 647.62 502.646L414.038 37.9539C402.316 14.6347 378.994 0 353.554 0Z" fill="white"/>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 608 B |
|
After Width: | Height: | Size: 52 KiB |
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
"fill" : {
|
||||||
|
"automatic-gradient" : "extended-srgb:0.00000,0.47843,1.00000,1.00000"
|
||||||
|
},
|
||||||
|
"groups" : [
|
||||||
|
{
|
||||||
|
"layers" : [
|
||||||
|
{
|
||||||
|
"image-name" : "expo-symbol 2.svg",
|
||||||
|
"name" : "expo-symbol 2",
|
||||||
|
"position" : {
|
||||||
|
"scale" : 1,
|
||||||
|
"translation-in-points" : [
|
||||||
|
1.1008400065293245e-05,
|
||||||
|
-16.046875
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"image-name" : "grid.png",
|
||||||
|
"name" : "grid"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"shadow" : {
|
||||||
|
"kind" : "neutral",
|
||||||
|
"opacity" : 0.5
|
||||||
|
},
|
||||||
|
"translucency" : {
|
||||||
|
"enabled" : true,
|
||||||
|
"value" : 0.5
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"supported-platforms" : {
|
||||||
|
"circles" : [
|
||||||
|
"watchOS"
|
||||||
|
],
|
||||||
|
"squares" : "shared"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 17 KiB |
|
After Width: | Height: | Size: 77 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 780 KiB |
|
After Width: | Height: | Size: 324 KiB |
|
After Width: | Height: | Size: 6.2 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
After Width: | Height: | Size: 21 KiB |
|
After Width: | Height: | Size: 3.2 KiB |
|
After Width: | Height: | Size: 215 B |
|
After Width: | Height: | Size: 347 B |
|
After Width: | Height: | Size: 468 B |
|
After Width: | Height: | Size: 253 B |
|
After Width: | Height: | Size: 343 B |
|
After Width: | Height: | Size: 479 B |
|
After Width: | Height: | Size: 58 KiB |
@@ -0,0 +1,19 @@
|
|||||||
|
// Learn more: https://docs.expo.dev/guides/monorepos/
|
||||||
|
const { getDefaultConfig } = require('expo/metro-config');
|
||||||
|
const path = require('path');
|
||||||
|
|
||||||
|
const projectRoot = __dirname;
|
||||||
|
const monorepoRoot = path.resolve(projectRoot, '../..');
|
||||||
|
|
||||||
|
const config = getDefaultConfig(projectRoot);
|
||||||
|
|
||||||
|
// Watch the whole monorepo so changes in packages/* trigger rebuilds.
|
||||||
|
config.watchFolders = [monorepoRoot];
|
||||||
|
|
||||||
|
// Resolve modules from the app's node_modules first, then the workspace root.
|
||||||
|
config.resolver.nodeModulesPaths = [
|
||||||
|
path.resolve(projectRoot, 'node_modules'),
|
||||||
|
path.resolve(monorepoRoot, 'node_modules'),
|
||||||
|
];
|
||||||
|
|
||||||
|
module.exports = config;
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
"name": "mobile",
|
||||||
|
"main": "expo-router/entry",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@expo/ui": "~56.0.14",
|
||||||
|
"@<@var(context.project.name)>/rpc": "workspace:*",
|
||||||
|
"expo": "~56.0.5",
|
||||||
|
"expo-constants": "~56.0.16",
|
||||||
|
"expo-crypto": "~56.0.4",
|
||||||
|
"expo-dev-client": "~56.0.18",
|
||||||
|
"expo-device": "~56.0.4",
|
||||||
|
"expo-font": "~56.0.5",
|
||||||
|
"expo-glass-effect": "~56.0.4",
|
||||||
|
"expo-image": "~56.0.9",
|
||||||
|
"expo-linking": "~56.0.12",
|
||||||
|
"expo-router": "~56.2.7",
|
||||||
|
"expo-splash-screen": "~56.0.10",
|
||||||
|
"expo-status-bar": "~56.0.4",
|
||||||
|
"expo-symbols": "~56.0.5",
|
||||||
|
"expo-system-ui": "~56.0.5",
|
||||||
|
"expo-web-browser": "~56.0.5",
|
||||||
|
"react": "19.2.3",
|
||||||
|
"react-dom": "19.2.3",
|
||||||
|
"react-native": "0.85.3",
|
||||||
|
"react-native-gesture-handler": "~2.31.1",
|
||||||
|
"react-native-reanimated": "4.3.1",
|
||||||
|
"react-native-safe-area-context": "~5.7.0",
|
||||||
|
"react-native-screens": "4.25.2",
|
||||||
|
"react-native-web": "~0.21.0",
|
||||||
|
"react-native-worklets": "0.8.3"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/react": "~19.2.2",
|
||||||
|
"typescript": "~6.0.3"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"start": "expo start",
|
||||||
|
"reset-project": "node ./scripts/reset-project.js",
|
||||||
|
"android": "expo run:android",
|
||||||
|
"ios": "expo run:ios",
|
||||||
|
"web": "expo start --web",
|
||||||
|
"lint": "expo lint"
|
||||||
|
},
|
||||||
|
"private": true
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
import { Stack } from "expo-router";
|
||||||
|
|
||||||
|
export default function RootLayout() {
|
||||||
|
return <Stack screenOptions={{headerShown:false}} />;
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
import { createRouter, Todo } from "@<@var(context.project.name)>/rpc"
|
||||||
|
import { useEffect, useState } from "react"
|
||||||
|
import { ScrollView, Button, Checkbox, Host, Column, TextInput, Text, useNativeState, Row, Icon } from "@expo/ui";
|
||||||
|
import * as Crypto from "expo-crypto"
|
||||||
|
import { colorInvert, controlSize } from "@expo/ui/swift-ui/modifiers";
|
||||||
|
import { fillMaxWidth, padding, width } from "@expo/ui/jetpack-compose/modifiers";
|
||||||
|
export default function Index() {
|
||||||
|
const router = createRouter("http://10.0.2.2:8080")
|
||||||
|
const [todos, setTodos] = useState<Array<Todo>>([])
|
||||||
|
const todoToCreateTask = useNativeState<string>("")
|
||||||
|
const fetchTodos = () => {
|
||||||
|
router.todos.listTodos({}).then((r) => {
|
||||||
|
setTodos(r.todos)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
useEffect(() => {
|
||||||
|
fetchTodos();
|
||||||
|
})
|
||||||
|
|
||||||
|
const setTodoDone = (id: string, task: string) => {
|
||||||
|
return (done: boolean) => {
|
||||||
|
router.todos.updateTodo({ todo: { id, task, done } })
|
||||||
|
fetchTodos()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const deleteTodo = (id: string) => {
|
||||||
|
return () => {
|
||||||
|
router.todos.deleteTodo({ todo: { id } })
|
||||||
|
fetchTodos()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const createTodo = () => {
|
||||||
|
router.todos.createTodo({ todo: { id: Crypto.randomUUID(), task: todoToCreateTask.value } }).then((r) => {
|
||||||
|
console.log(r)
|
||||||
|
fetchTodos()
|
||||||
|
}).catch((e) => {
|
||||||
|
console.log(e)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateTodoToCreateTask = (task: string) => {
|
||||||
|
todoToCreateTask.value = task
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Host style={{ flex: 1 }}>
|
||||||
|
<Column alignment="center" modifiers={[fillMaxWidth()]}>
|
||||||
|
<Text textStyle={{fontSize:30}}> Create Todo </Text>
|
||||||
|
<TextInput value={todoToCreateTask} onChangeText={updateTodoToCreateTask} />
|
||||||
|
<Button modifiers={[controlSize('regular')]} label="create Todo" onPress={createTodo} />
|
||||||
|
<ScrollView modifiers={[fillMaxWidth()]}>
|
||||||
|
{
|
||||||
|
todos.map((todo) => (
|
||||||
|
<Row alignment="center" modifiers={[fillMaxWidth(),padding(20,0,20,0)]}>
|
||||||
|
<Column alignment="start" modifiers={[width(200)]}>
|
||||||
|
<Text> {todo.task} </Text>
|
||||||
|
</Column>
|
||||||
|
<Column alignment="center">
|
||||||
|
<Checkbox value={todo.done || false} onValueChange={setTodoDone(todo.id || "", todo.task)} />
|
||||||
|
</Column>
|
||||||
|
<Column alignment="end" modifiers={[fillMaxWidth()]}>
|
||||||
|
<Button modifiers={[controlSize('regular')]} label="delete" onPress={deleteTodo(todo.id || "")}/>
|
||||||
|
</Column>
|
||||||
|
</Row>
|
||||||
|
))
|
||||||
|
}
|
||||||
|
</ScrollView>
|
||||||
|
</Column>
|
||||||
|
</Host>
|
||||||
|
)
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"extends": "expo/tsconfig.base",
|
||||||
|
"compilerOptions": {
|
||||||
|
"strict": true,
|
||||||
|
"paths": {
|
||||||
|
"@/*": [
|
||||||
|
"./src/*"
|
||||||
|
],
|
||||||
|
"@/assets/*": [
|
||||||
|
"./assets/*"
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"**/*.ts",
|
||||||
|
"**/*.tsx",
|
||||||
|
".expo/types/**/*.ts",
|
||||||
|
"expo-env.d.ts"
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
node_modules
|
|
||||||
|
|
||||||
# Output
|
|
||||||
.output
|
|
||||||
.vercel
|
|
||||||
.netlify
|
|
||||||
.wrangler
|
|
||||||
/.svelte-kit
|
|
||||||
/build
|
|
||||||
|
|
||||||
# OS
|
|
||||||
.DS_Store
|
|
||||||
Thumbs.db
|
|
||||||
|
|
||||||
# Env
|
|
||||||
.env
|
|
||||||
.env.*
|
|
||||||
!.env.example
|
|
||||||
!.env.test
|
|
||||||
|
|
||||||
# Vite
|
|
||||||
vite.config.js.timestamp-*
|
|
||||||
vite.config.ts.timestamp-*
|
|
||||||
# Paraglide
|
|
||||||
src/lib/paraglide
|
|
||||||
project.inlang/cache/
|
|
||||||
@@ -1,9 +0,0 @@
|
|||||||
# Package Managers
|
|
||||||
package-lock.json
|
|
||||||
pnpm-lock.yaml
|
|
||||||
yarn.lock
|
|
||||||
bun.lock
|
|
||||||
bun.lockb
|
|
||||||
|
|
||||||
# Miscellaneous
|
|
||||||
/static/
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
"useTabs": true,
|
|
||||||
"singleQuote": true,
|
|
||||||
"trailingComma": "none",
|
|
||||||
"printWidth": 100,
|
|
||||||
"plugins": ["prettier-plugin-svelte", "prettier-plugin-tailwindcss"],
|
|
||||||
"overrides": [
|
|
||||||
{
|
|
||||||
"files": "*.svelte",
|
|
||||||
"options": {
|
|
||||||
"parser": "svelte"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"tailwindStylesheet": "./src/routes/layout.css"
|
|
||||||
}
|
|
||||||
3
template/apps/web/.vscode/extensions.json
vendored
@@ -1,3 +0,0 @@
|
|||||||
{
|
|
||||||
"recommendations": ["svelte.svelte-vscode", "esbenp.prettier-vscode", "bradlc.vscode-tailwindcss"]
|
|
||||||
}
|
|
||||||
5
template/apps/web/.vscode/settings.json
vendored
@@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"files.associations": {
|
|
||||||
"*.css": "tailwindcss"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,42 +0,0 @@
|
|||||||
# sv
|
|
||||||
|
|
||||||
Everything you need to build a Svelte project, powered by [`sv`](https://github.com/sveltejs/cli).
|
|
||||||
|
|
||||||
## Creating a project
|
|
||||||
|
|
||||||
If you're seeing this, you've probably already done this step. Congrats!
|
|
||||||
|
|
||||||
```sh
|
|
||||||
# create a new project
|
|
||||||
npx sv create my-app
|
|
||||||
```
|
|
||||||
|
|
||||||
To recreate this project with the same configuration:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
# recreate this project
|
|
||||||
bun x sv@0.14.0 create --template minimal --types ts --add prettier tailwindcss="plugins:typography" paraglide="languageTags:en, de-De+demo:no" --install bun web
|
|
||||||
```
|
|
||||||
|
|
||||||
## Developing
|
|
||||||
|
|
||||||
Once you've created a project and installed dependencies with `npm install` (or `pnpm install` or `yarn`), start a development server:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm run dev
|
|
||||||
|
|
||||||
# or start the server and open the app in a new browser tab
|
|
||||||
npm run dev -- --open
|
|
||||||
```
|
|
||||||
|
|
||||||
## Building
|
|
||||||
|
|
||||||
To create a production version of your app:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm run build
|
|
||||||
```
|
|
||||||
|
|
||||||
You can preview the production build with `npm run preview`.
|
|
||||||
|
|
||||||
> To deploy your app, you may need to install an [adapter](https://svelte.dev/docs/kit/adapters) for your target environment.
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://shadcn-svelte.com/schema.json",
|
|
||||||
"tailwind": {
|
|
||||||
"css": "src/routes/layout.css",
|
|
||||||
"baseColor": "mist"
|
|
||||||
},
|
|
||||||
"aliases": {
|
|
||||||
"components": "$lib/components",
|
|
||||||
"utils": "$lib/utils",
|
|
||||||
"ui": "$lib/components/ui",
|
|
||||||
"hooks": "$lib/hooks",
|
|
||||||
"lib": "$lib"
|
|
||||||
},
|
|
||||||
"typescript": true,
|
|
||||||
"registry": "https://shadcn-svelte.com/registry",
|
|
||||||
"style": "lyra",
|
|
||||||
"iconLibrary": "lucide",
|
|
||||||
"menuColor": "default",
|
|
||||||
"menuAccent": "subtle"
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://inlang.com/schema/inlang-message-format",
|
|
||||||
"hello_world": "Hello, {name} from de-de!"
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://inlang.com/schema/inlang-message-format",
|
|
||||||
"hello_world": "Hello, {name} from en!"
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "web",
|
|
||||||
"private": true,
|
|
||||||
"version": "0.0.1",
|
|
||||||
"type": "module",
|
|
||||||
"scripts": {
|
|
||||||
"dev": "vite dev",
|
|
||||||
"build": "vite build",
|
|
||||||
"preview": "vite preview",
|
|
||||||
"prepare": "svelte-kit sync || echo ''",
|
|
||||||
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
||||||
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
||||||
"lint": "prettier --check .",
|
|
||||||
"format": "prettier --write ."
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"@fontsource-variable/roboto": "^5.2.10",
|
|
||||||
"@inlang/paraglide-js": "^2.10.0",
|
|
||||||
"@internationalized/date": "^3.12.0",
|
|
||||||
"@lucide/svelte": "^0.577.0",
|
|
||||||
"@sveltejs/adapter-auto": "^7.0.0",
|
|
||||||
"@sveltejs/kit": "^2.50.2",
|
|
||||||
"@sveltejs/vite-plugin-svelte": "^6.2.4",
|
|
||||||
"@tailwindcss/typography": "^0.5.19",
|
|
||||||
"@tailwindcss/vite": "^4.1.18",
|
|
||||||
"@tanstack/table-core": "^8.21.3",
|
|
||||||
"bits-ui": "^2.16.3",
|
|
||||||
"clsx": "^2.1.1",
|
|
||||||
"embla-carousel-svelte": "^8.6.0",
|
|
||||||
"formsnap": "^2.0.1",
|
|
||||||
"layerchart": "2.0.0-next.48",
|
|
||||||
"mode-watcher": "^1.1.0",
|
|
||||||
"paneforge": "^1.0.2",
|
|
||||||
"prettier": "^3.8.1",
|
|
||||||
"prettier-plugin-svelte": "^3.4.1",
|
|
||||||
"prettier-plugin-tailwindcss": "^0.7.2",
|
|
||||||
"shadcn-svelte": "^1.2.7",
|
|
||||||
"svelte": "^5.54.0",
|
|
||||||
"svelte-check": "^4.4.2",
|
|
||||||
"svelte-sonner": "^1.1.0",
|
|
||||||
"sveltekit-superforms": "^2.30.0",
|
|
||||||
"tailwind-merge": "^3.5.0",
|
|
||||||
"tailwind-variants": "^3.2.2",
|
|
||||||
"tailwindcss": "^4.1.18",
|
|
||||||
"tw-animate-css": "^1.4.0",
|
|
||||||
"typescript": "^5.9.3",
|
|
||||||
"vaul-svelte": "^1.0.0-next.7",
|
|
||||||
"vite": "^7.3.1"
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"@bufbuild/protobuf": "^2.11.0",
|
|
||||||
"@connectrpc/connect": "^2.1.1",
|
|
||||||
"@connectrpc/connect-web": "^2.1.1",
|
|
||||||
"@<@var(context.project.name)>/rpc": "workspace:*",
|
|
||||||
"@tanstack/query-db-collection": "^1.0.33",
|
|
||||||
"@tanstack/svelte-db": "^0.1.79",
|
|
||||||
"@tanstack/svelte-query": "^6.1.13"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://inlang.com/schema/project-settings",
|
|
||||||
"modules": [
|
|
||||||
"https://cdn.jsdelivr.net/npm/@inlang/plugin-message-format@4/dist/index.js",
|
|
||||||
"https://cdn.jsdelivr.net/npm/@inlang/plugin-m-function-matcher@2/dist/index.js"
|
|
||||||
],
|
|
||||||
"plugin.inlang.messageFormat": {
|
|
||||||
"pathPattern": "./messages/{locale}.json"
|
|
||||||
},
|
|
||||||
"baseLocale": "en",
|
|
||||||
"locales": ["en", "de-de"]
|
|
||||||
}
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
import type { Handle } from '@sveltejs/kit';
|
|
||||||
import { getTextDirection } from '$lib/paraglide/runtime';
|
|
||||||
import { paraglideMiddleware } from '$lib/paraglide/server';
|
|
||||||
|
|
||||||
const handleParaglide: Handle = ({ event, resolve }) =>
|
|
||||||
paraglideMiddleware(event.request, ({ request, locale }) => {
|
|
||||||
event.request = request;
|
|
||||||
|
|
||||||
return resolve(event, {
|
|
||||||
transformPageChunk: ({ html }) =>
|
|
||||||
html
|
|
||||||
.replace('%paraglide.lang%', locale)
|
|
||||||
.replace('%paraglide.dir%', getTextDirection(locale))
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
export const handle: Handle = handleParaglide;
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
import type { Reroute } from '@sveltejs/kit';
|
|
||||||
import { deLocalizeUrl } from '$lib/paraglide/runtime';
|
|
||||||
|
|
||||||
export const reroute: Reroute = (request) => deLocalizeUrl(request.url).pathname;
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import type { Todo } from "@<@var(context.project.name)>/rpc";
|
|
||||||
import type { ExtractPayload } from "$lib/utils"
|
|
||||||
import Input from "$lib/components/ui/input/input.svelte";
|
|
||||||
import Button from "$lib/components/ui/button/button.svelte"
|
|
||||||
import * as Field from "$lib/components/ui/field/index"
|
|
||||||
import { getTodoCollection } from "$lib/todocollectionscontext";
|
|
||||||
const todoCollection = getTodoCollection();
|
|
||||||
let todo = $state<ExtractPayload<Todo>>({id: crypto.randomUUID() ,task: "",done:false})
|
|
||||||
let create = () => {
|
|
||||||
todoCollection.insert(todo)
|
|
||||||
todo = { id: crypto.randomUUID(), task: "", done:false }
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div class="flex flex-col items-center justify-center">
|
|
||||||
<Field.FieldGroup class="w-50">
|
|
||||||
<Field.Field>
|
|
||||||
<Field.Label>
|
|
||||||
Todo
|
|
||||||
</Field.Label>
|
|
||||||
<Input bind:value={todo.task}/>
|
|
||||||
</Field.Field>
|
|
||||||
<Button variant="outline" onclick={create} > Create </Button>
|
|
||||||
</Field.FieldGroup>
|
|
||||||
</div>
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { getTodoCollection } from '$lib/todocollectionscontext';
|
|
||||||
import { useLiveQuery } from '@tanstack/svelte-db';
|
|
||||||
import { tick } from 'svelte';
|
|
||||||
import { ScrollArea } from '../ui/scroll-area/index';
|
|
||||||
import type { Todo as TodoRpc } from '@<@var(context.project.name)>/rpc';
|
|
||||||
import { browser } from '$app/environment';
|
|
||||||
import Todo from './Todo.svelte';
|
|
||||||
|
|
||||||
let { initialTodos }: { initialTodos: TodoRpc[] } = $props();
|
|
||||||
const todoCollection = getTodoCollection();
|
|
||||||
const data = useLiveQuery((q) => q.from({ todos: todoCollection }));
|
|
||||||
const todos = $derived.by(() => {
|
|
||||||
const res = data.data.toSorted((a, b) => {
|
|
||||||
if (a.done != b.done) {
|
|
||||||
return a.done ? -1 : 1;
|
|
||||||
}
|
|
||||||
if (!a.done && !b.done) {
|
|
||||||
let adate = a.createdAt ? new Date(a.createdAt) : new Date(Date.now());
|
|
||||||
let bdate = b.createdAt ? new Date(b.createdAt) : new Date(Date.now());
|
|
||||||
return bdate.getTime() - adate.getTime();
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
});
|
|
||||||
return res;
|
|
||||||
});
|
|
||||||
let scrollerRef = $state<HTMLElement | null>(null);
|
|
||||||
$effect(() => {
|
|
||||||
todos;
|
|
||||||
tick().then(() => {
|
|
||||||
scrollerRef?.scrollTo({ top: scrollerRef.scrollHeight, behavior: 'smooth' });
|
|
||||||
});
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<ScrollArea
|
|
||||||
bind:viewportRef={scrollerRef}
|
|
||||||
id="scroller"
|
|
||||||
class="flex h-[80%] w-full flex-col items-center justify-center lg:w-[30%]"
|
|
||||||
>
|
|
||||||
{#if browser}
|
|
||||||
{#each todos as todo (todo.id)}
|
|
||||||
<Todo {todo} />
|
|
||||||
{/each}
|
|
||||||
{:else}
|
|
||||||
{#each initialTodos as todo}
|
|
||||||
<Todo {todo} />
|
|
||||||
{/each}
|
|
||||||
{/if}
|
|
||||||
</ScrollArea>
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { type Todo } from "@<@var(context.project.name)>/rpc";
|
|
||||||
import { getTodoCollection } from "$lib/todocollectionscontext";
|
|
||||||
import * as Card from "../ui/card/index";
|
|
||||||
import * as Field from "../ui/field/index";
|
|
||||||
import Input from "../ui/input/input.svelte";
|
|
||||||
import type { ExtractPayload } from "$lib/utils";
|
|
||||||
import Checkbox from "../ui/checkbox/checkbox.svelte";
|
|
||||||
import Button from "../ui/button/button.svelte";
|
|
||||||
import { DeleteIcon } from "@lucide/svelte"
|
|
||||||
let { todo } : { todo:ExtractPayload<Todo> } = $props();
|
|
||||||
let todoState = $state(todo)
|
|
||||||
const todoCollection = getTodoCollection();
|
|
||||||
const update = () => {
|
|
||||||
todoCollection.update(todoState.id,(draft) => {
|
|
||||||
draft.done = todoState.done;
|
|
||||||
draft.task = todoState.task;
|
|
||||||
})
|
|
||||||
}
|
|
||||||
const del = () => {
|
|
||||||
if (todoState.id) {
|
|
||||||
todoCollection.delete(todoState.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<Card.Card class="p-5" onkeydown={()=>update()}>
|
|
||||||
<Card.CardContent>
|
|
||||||
<Field.FieldSet>
|
|
||||||
<Field.Field>
|
|
||||||
<Field.Label>
|
|
||||||
task
|
|
||||||
</Field.Label>
|
|
||||||
<Field.FieldContent>
|
|
||||||
<Input bind:value={todoState.task}/>
|
|
||||||
</Field.FieldContent>
|
|
||||||
</Field.Field>
|
|
||||||
<Field.Field>
|
|
||||||
<Field.Label>
|
|
||||||
done
|
|
||||||
</Field.Label>
|
|
||||||
<Field.FieldContent onclick={()=>update()}>
|
|
||||||
<Checkbox bind:checked={todoState.done}/>
|
|
||||||
</Field.FieldContent>
|
|
||||||
</Field.Field>
|
|
||||||
<div class="flex flex-row">
|
|
||||||
<Field.Field>
|
|
||||||
<Field.Label>
|
|
||||||
Created
|
|
||||||
</Field.Label>
|
|
||||||
<Field.FieldContent>
|
|
||||||
{(new Date(todoState.createdAt||"now")).toLocaleString()}
|
|
||||||
</Field.FieldContent>
|
|
||||||
</Field.Field>
|
|
||||||
<Field.Field>
|
|
||||||
<Field.Label>
|
|
||||||
Updated
|
|
||||||
</Field.Label>
|
|
||||||
<Field.FieldContent>
|
|
||||||
{(new Date(todoState.updatesAt||"now")).toLocaleString()}
|
|
||||||
</Field.FieldContent>
|
|
||||||
</Field.Field>
|
|
||||||
<Button variant='destructive' onclick={del}>
|
|
||||||
<DeleteIcon/>
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</Field.FieldSet>
|
|
||||||
</Card.CardContent>
|
|
||||||
</Card.Card>
|
|
||||||
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Accordion as AccordionPrimitive } from "bits-ui";
|
|
||||||
import { cn, type WithoutChild } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
children,
|
|
||||||
...restProps
|
|
||||||
}: WithoutChild<AccordionPrimitive.ContentProps> = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AccordionPrimitive.Content
|
|
||||||
bind:ref
|
|
||||||
data-slot="accordion-content"
|
|
||||||
class="data-open:animate-accordion-down data-closed:animate-accordion-up text-xs overflow-hidden"
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
<div
|
|
||||||
class={cn(
|
|
||||||
"pt-0 pb-2.5 [&_a]:hover:text-foreground [&_a]:underline [&_a]:underline-offset-3 [&_p:not(:last-child)]:mb-4",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</div>
|
|
||||||
</AccordionPrimitive.Content>
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Accordion as AccordionPrimitive } from "bits-ui";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: AccordionPrimitive.ItemProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AccordionPrimitive.Item
|
|
||||||
bind:ref
|
|
||||||
data-slot="accordion-item"
|
|
||||||
class={cn("not-last:border-b", className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Accordion as AccordionPrimitive } from "bits-ui";
|
|
||||||
import { cn, type WithoutChild } from "$lib/utils.js";
|
|
||||||
import ChevronDownIcon from '@lucide/svelte/icons/chevron-down';
|
|
||||||
import ChevronUpIcon from '@lucide/svelte/icons/chevron-up';
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
level = 3,
|
|
||||||
children,
|
|
||||||
...restProps
|
|
||||||
}: WithoutChild<AccordionPrimitive.TriggerProps> & {
|
|
||||||
level?: AccordionPrimitive.HeaderProps["level"];
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AccordionPrimitive.Header {level} class="flex">
|
|
||||||
<AccordionPrimitive.Trigger
|
|
||||||
data-slot="accordion-trigger"
|
|
||||||
bind:ref
|
|
||||||
class={cn(
|
|
||||||
"focus-visible:ring-ring/50 focus-visible:border-ring focus-visible:after:border-ring **:data-[slot=accordion-trigger-icon]:text-muted-foreground rounded-none py-2.5 text-left text-xs font-medium hover:underline focus-visible:ring-1 **:data-[slot=accordion-trigger-icon]:ml-auto **:data-[slot=accordion-trigger-icon]:size-4 group/accordion-trigger relative flex flex-1 items-start justify-between border border-transparent transition-all outline-none disabled:pointer-events-none disabled:opacity-50",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
<ChevronDownIcon data-slot="accordion-trigger-icon" class="cn-accordion-trigger-icon pointer-events-none shrink-0 group-aria-expanded/accordion-trigger:hidden" />
|
|
||||||
<ChevronUpIcon data-slot="accordion-trigger-icon" class="cn-accordion-trigger-icon pointer-events-none hidden shrink-0 group-aria-expanded/accordion-trigger:inline" />
|
|
||||||
</AccordionPrimitive.Trigger>
|
|
||||||
</AccordionPrimitive.Header>
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { Accordion as AccordionPrimitive } from "bits-ui";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
value = $bindable(),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: AccordionPrimitive.RootProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AccordionPrimitive.Root
|
|
||||||
bind:ref
|
|
||||||
bind:value={value as never}
|
|
||||||
data-slot="accordion"
|
|
||||||
class={cn("cn-accordion flex w-full flex-col", className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
import Root from "./accordion.svelte";
|
|
||||||
import Content from "./accordion-content.svelte";
|
|
||||||
import Item from "./accordion-item.svelte";
|
|
||||||
import Trigger from "./accordion-trigger.svelte";
|
|
||||||
|
|
||||||
export {
|
|
||||||
Root,
|
|
||||||
Content,
|
|
||||||
Item,
|
|
||||||
Trigger,
|
|
||||||
//
|
|
||||||
Root as Accordion,
|
|
||||||
Content as AccordionContent,
|
|
||||||
Item as AccordionItem,
|
|
||||||
Trigger as AccordionTrigger,
|
|
||||||
};
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
|
||||||
import {
|
|
||||||
buttonVariants,
|
|
||||||
type ButtonVariant,
|
|
||||||
type ButtonSize,
|
|
||||||
} from "$lib/components/ui/button/index.js";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
variant = "default",
|
|
||||||
size = "default",
|
|
||||||
...restProps
|
|
||||||
}: AlertDialogPrimitive.ActionProps & {
|
|
||||||
variant?: ButtonVariant;
|
|
||||||
size?: ButtonSize;
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AlertDialogPrimitive.Action
|
|
||||||
bind:ref
|
|
||||||
data-slot="alert-dialog-action"
|
|
||||||
class={cn(buttonVariants({ variant, size }), "cn-alert-dialog-action", className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
|
||||||
import {
|
|
||||||
buttonVariants,
|
|
||||||
type ButtonVariant,
|
|
||||||
type ButtonSize,
|
|
||||||
} from "$lib/components/ui/button/index.js";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
variant = "outline",
|
|
||||||
size = "default",
|
|
||||||
...restProps
|
|
||||||
}: AlertDialogPrimitive.CancelProps & {
|
|
||||||
variant?: ButtonVariant;
|
|
||||||
size?: ButtonSize;
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AlertDialogPrimitive.Cancel
|
|
||||||
bind:ref
|
|
||||||
data-slot="alert-dialog-cancel"
|
|
||||||
class={cn(buttonVariants({ variant, size }), "cn-alert-dialog-cancel", className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
|
||||||
import AlertDialogPortal from "./alert-dialog-portal.svelte";
|
|
||||||
import AlertDialogOverlay from "./alert-dialog-overlay.svelte";
|
|
||||||
import { cn, type WithoutChild, type WithoutChildrenOrChild } from "$lib/utils.js";
|
|
||||||
import type { ComponentProps } from "svelte";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
size = "default",
|
|
||||||
portalProps,
|
|
||||||
...restProps
|
|
||||||
}: WithoutChild<AlertDialogPrimitive.ContentProps> & {
|
|
||||||
size?: "default" | "sm";
|
|
||||||
portalProps?: WithoutChildrenOrChild<ComponentProps<typeof AlertDialogPortal>>;
|
|
||||||
} = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AlertDialogPortal {...portalProps}>
|
|
||||||
<AlertDialogOverlay />
|
|
||||||
<AlertDialogPrimitive.Content
|
|
||||||
bind:ref
|
|
||||||
data-slot="alert-dialog-content"
|
|
||||||
data-size={size}
|
|
||||||
class={cn(
|
|
||||||
"data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 data-closed:zoom-out-95 data-open:zoom-in-95 bg-popover text-popover-foreground ring-foreground/10 gap-4 rounded-none p-4 ring-1 duration-100 data-[size=default]:max-w-xs data-[size=sm]:max-w-xs data-[size=default]:sm:max-w-sm group/alert-dialog-content fixed top-1/2 left-1/2 z-50 grid w-full -translate-x-1/2 -translate-y-1/2 outline-none",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
</AlertDialogPortal>
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: AlertDialogPrimitive.DescriptionProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AlertDialogPrimitive.Description
|
|
||||||
bind:ref
|
|
||||||
data-slot="alert-dialog-description"
|
|
||||||
class={cn("text-muted-foreground *:[a]:hover:text-foreground text-xs/relaxed text-balance md:text-pretty *:[a]:underline *:[a]:underline-offset-3", className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
||||||
import type { HTMLAttributes } from "svelte/elements";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
children,
|
|
||||||
...restProps
|
|
||||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div
|
|
||||||
bind:this={ref}
|
|
||||||
data-slot="alert-dialog-footer"
|
|
||||||
class={cn(
|
|
||||||
"cn-alert-dialog-footer flex flex-col-reverse gap-2 group-data-[size=sm]/alert-dialog-content:grid group-data-[size=sm]/alert-dialog-content:grid-cols-2 sm:flex-row sm:justify-end",
|
|
||||||
className
|
|
||||||
)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</div>
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import type { HTMLAttributes } from "svelte/elements";
|
|
||||||
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
children,
|
|
||||||
...restProps
|
|
||||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div
|
|
||||||
bind:this={ref}
|
|
||||||
data-slot="alert-dialog-header"
|
|
||||||
class={cn("grid grid-rows-[auto_1fr] place-items-center gap-1.5 text-center has-data-[slot=alert-dialog-media]:grid-rows-[auto_auto_1fr] has-data-[slot=alert-dialog-media]:gap-x-4 sm:group-data-[size=default]/alert-dialog-content:place-items-start sm:group-data-[size=default]/alert-dialog-content:text-left sm:group-data-[size=default]/alert-dialog-content:has-data-[slot=alert-dialog-media]:grid-rows-[auto_1fr]", className)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</div>
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import type { HTMLAttributes } from "svelte/elements";
|
|
||||||
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
children,
|
|
||||||
...restProps
|
|
||||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div
|
|
||||||
bind:this={ref}
|
|
||||||
data-slot="alert-dialog-media"
|
|
||||||
class={cn("bg-muted mb-2 inline-flex size-10 items-center justify-center rounded-none sm:group-data-[size=default]/alert-dialog-content:row-span-2 *:[svg:not([class*='size-'])]:size-6", className)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</div>
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: AlertDialogPrimitive.OverlayProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AlertDialogPrimitive.Overlay
|
|
||||||
bind:ref
|
|
||||||
data-slot="alert-dialog-overlay"
|
|
||||||
class={cn("data-open:animate-in data-closed:animate-out data-closed:fade-out-0 data-open:fade-in-0 bg-black/10 duration-100 supports-backdrop-filter:backdrop-blur-xs fixed inset-0 z-50", className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
|
||||||
|
|
||||||
let { ...restProps }: AlertDialogPrimitive.PortalProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AlertDialogPrimitive.Portal {...restProps} />
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
|
||||||
import { cn } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
...restProps
|
|
||||||
}: AlertDialogPrimitive.TitleProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AlertDialogPrimitive.Title
|
|
||||||
bind:ref
|
|
||||||
data-slot="alert-dialog-title"
|
|
||||||
class={cn("text-sm font-medium sm:group-data-[size=default]/alert-dialog-content:group-has-data-[slot=alert-dialog-media]/alert-dialog-content:col-start-2", className)}
|
|
||||||
{...restProps}
|
|
||||||
/>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
|
||||||
|
|
||||||
let { ref = $bindable(null), ...restProps }: AlertDialogPrimitive.TriggerProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AlertDialogPrimitive.Trigger bind:ref data-slot="alert-dialog-trigger" {...restProps} />
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import { AlertDialog as AlertDialogPrimitive } from "bits-ui";
|
|
||||||
|
|
||||||
let { open = $bindable(false), ...restProps }: AlertDialogPrimitive.RootProps = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<AlertDialogPrimitive.Root bind:open {...restProps} />
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
import Root from "./alert-dialog.svelte";
|
|
||||||
import Portal from "./alert-dialog-portal.svelte";
|
|
||||||
import Trigger from "./alert-dialog-trigger.svelte";
|
|
||||||
import Title from "./alert-dialog-title.svelte";
|
|
||||||
import Action from "./alert-dialog-action.svelte";
|
|
||||||
import Cancel from "./alert-dialog-cancel.svelte";
|
|
||||||
import Footer from "./alert-dialog-footer.svelte";
|
|
||||||
import Header from "./alert-dialog-header.svelte";
|
|
||||||
import Overlay from "./alert-dialog-overlay.svelte";
|
|
||||||
import Content from "./alert-dialog-content.svelte";
|
|
||||||
import Description from "./alert-dialog-description.svelte";
|
|
||||||
import Media from "./alert-dialog-media.svelte";
|
|
||||||
|
|
||||||
export {
|
|
||||||
Root,
|
|
||||||
Title,
|
|
||||||
Action,
|
|
||||||
Cancel,
|
|
||||||
Portal,
|
|
||||||
Footer,
|
|
||||||
Header,
|
|
||||||
Trigger,
|
|
||||||
Overlay,
|
|
||||||
Content,
|
|
||||||
Description,
|
|
||||||
Media,
|
|
||||||
//
|
|
||||||
Root as AlertDialog,
|
|
||||||
Title as AlertDialogTitle,
|
|
||||||
Action as AlertDialogAction,
|
|
||||||
Cancel as AlertDialogCancel,
|
|
||||||
Portal as AlertDialogPortal,
|
|
||||||
Footer as AlertDialogFooter,
|
|
||||||
Header as AlertDialogHeader,
|
|
||||||
Trigger as AlertDialogTrigger,
|
|
||||||
Overlay as AlertDialogOverlay,
|
|
||||||
Content as AlertDialogContent,
|
|
||||||
Description as AlertDialogDescription,
|
|
||||||
Media as AlertDialogMedia,
|
|
||||||
};
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
<script lang="ts">
|
|
||||||
import type { HTMLAttributes } from "svelte/elements";
|
|
||||||
import { cn, type WithElementRef } from "$lib/utils.js";
|
|
||||||
|
|
||||||
let {
|
|
||||||
ref = $bindable(null),
|
|
||||||
class: className,
|
|
||||||
children,
|
|
||||||
...restProps
|
|
||||||
}: WithElementRef<HTMLAttributes<HTMLDivElement>> = $props();
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<div
|
|
||||||
bind:this={ref}
|
|
||||||
data-slot="alert-action"
|
|
||||||
class={cn("absolute top-[calc(--spacing(1.25))] right-[calc(--spacing(1.25))]", className)}
|
|
||||||
{...restProps}
|
|
||||||
>
|
|
||||||
{@render children?.()}
|
|
||||||
</div>
|
|
||||||