From 515cc62735e25a66848ea592764cfc3c1b9044c0 Mon Sep 17 00:00:00 2001 From: Jiwon Kwon Date: Fri, 4 Sep 2026 19:35:17 +0900 Subject: [PATCH 1/7] Add Formisch and Valibot dependencies Add Formisch for SolidJS and its Valibot peer dependency to the web package. I limited this commit to dependency installation; Codex applied the manifest and lockfile changes and ran the web TypeScript check successfully. Assisted-by: Codex:gpt-5.6-sol --- packages/web/package.json | 5 +++ pnpm-lock.yaml | 82 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 87 insertions(+) diff --git a/packages/web/package.json b/packages/web/package.json index 684533e..e111fce 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -46,7 +46,11 @@ "preview": "vite preview" }, "dependencies": { +<<<<<<< HEAD "@kobalte/core": "^0.13.13", +======= + "@formisch/solid": "1.0.0", +>>>>>>> c537fd7 (Add Formisch and Valibot dependencies) "@solidjs/meta": "^0.29.4", "@solidjs/router": "^1.0.0", "@solidjs/start": "^2.0.0", @@ -54,6 +58,7 @@ "relay-runtime": "^21.0.1", "solid-js": "^1.9.5", "solid-relay": "1.0.0-beta.29", + "valibot": "1.4.2", "vite": "^8.2.0" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index adee240..4ac3b38 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -130,7 +130,11 @@ importers: version: 0.6.0-dev.263(@upyo/core@0.6.0-dev.263) drizzle-orm: specifier: 'catalog:' +<<<<<<< HEAD version: 1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.21.0)(postgres@3.4.9) +======= + version: 1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@types/pg@8.20.0)(pg@8.21.0)(valibot@1.4.2(typescript@7.0.2)) +>>>>>>> c537fd7 (Add Formisch and Valibot dependencies) graphql: specifier: 'catalog:' version: 16.14.2 @@ -179,7 +183,11 @@ importers: version: 4.13.0(graphql@16.14.2) '@pothos/plugin-drizzle': specifier: ^0.17.4 +<<<<<<< HEAD version: 0.17.4(@pothos/core@4.13.0(graphql@16.14.2))(drizzle-orm@1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.21.0)(postgres@3.4.9))(graphql@16.14.2) +======= + version: 0.17.4(@pothos/core@4.13.0(graphql@16.14.2))(drizzle-orm@1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@types/pg@8.20.0)(pg@8.21.0)(valibot@1.4.2(typescript@7.0.2)))(graphql@16.14.2) +>>>>>>> c537fd7 (Add Formisch and Valibot dependencies) '@pothos/plugin-errors': specifier: ^4.9.1 version: 4.9.1(@pothos/core@4.13.0(graphql@16.14.2))(graphql@16.14.2) @@ -197,7 +205,11 @@ importers: version: 0.6.0-dev.263(@upyo/core@0.6.0-dev.263) drizzle-orm: specifier: 'catalog:' +<<<<<<< HEAD version: 1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.21.0)(postgres@3.4.9) +======= + version: 1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@types/pg@8.20.0)(pg@8.21.0)(valibot@1.4.2(typescript@7.0.2)) +>>>>>>> c537fd7 (Add Formisch and Valibot dependencies) graphql: specifier: 'catalog:' version: 16.14.2 @@ -237,8 +249,13 @@ importers: version: 2.3.0-dev.840 drizzle-orm: specifier: 'catalog:' +<<<<<<< HEAD version: 1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.21.0)(postgres@3.4.9) postgres: +======= + version: 1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@types/pg@8.20.0)(pg@8.21.0)(valibot@1.4.2(typescript@7.0.2)) + pg: +>>>>>>> c537fd7 (Add Formisch and Valibot dependencies) specifier: 'catalog:' version: 3.4.9 devDependencies: @@ -260,9 +277,15 @@ importers: packages/web: dependencies: +<<<<<<< HEAD '@kobalte/core': specifier: ^0.13.13 version: 0.13.13(solid-js@1.9.14) +======= + '@formisch/solid': + specifier: 1.0.0 + version: 1.0.0(solid-js@1.9.14)(typescript@7.0.2)(valibot@1.4.2(typescript@7.0.2)) +>>>>>>> c537fd7 (Add Formisch and Valibot dependencies) '@solidjs/meta': specifier: ^0.29.4 version: 0.29.4(solid-js@1.9.14) @@ -284,6 +307,9 @@ importers: solid-relay: specifier: 1.0.0-beta.29 version: 1.0.0-beta.29(relay-runtime@21.0.1)(seroval@1.6.0)(solid-js@1.9.14) + valibot: + specifier: 1.4.2 + version: 1.4.2(typescript@7.0.2) vite: specifier: ^8.2.0 version: 8.2.0(@types/node@26.0.0)(esbuild@0.28.1)(jiti@2.7.0)(terser@5.48.0)(yaml@2.9.0) @@ -830,6 +856,7 @@ packages: resolution: {integrity: sha512-322xch1WhasP/vjH4yDPA1RnYwI6tlG72aH7fCpdFge8IC845urKEMET9LzJ2G5HfeCKAXPAcaZZEx9FXnTNrg==} engines: {bun: '>=1.1.0', deno: '>=2.0.0', node: '>=22.0.0'} +<<<<<<< HEAD '@fedify/uri-template@2.4.0-pr.1020.43': resolution: {integrity: sha512-2JsU5q+pDNMuoD3ZcJLwubpZWgixwcy0H4yVY/bpz/vEEEWu+g22pa6r52rloWvn5ARJXAkMxcyHDcBlZojpBg==} engines: {bun: '>=1.1.0', deno: '>=2.0.0', node: '>=22.0.0'} @@ -858,6 +885,17 @@ packages: '@floating-ui/utils@0.2.12': resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} +======= + '@formisch/solid@1.0.0': + resolution: {integrity: sha512-J5RF48YCM6SNYVkt+CJ0+5MHPC38wi3zgBM5hOpwnWFU5Q/gtdjb+vO63uJKWLAU7XQlnZVDo7rBzf8bVsvvcA==} + peerDependencies: + solid-js: '>=1.6 <2' + typescript: '>=5 <8' + valibot: '>=1.4.1 <2' + peerDependenciesMeta: + typescript: + optional: true +>>>>>>> c537fd7 (Add Formisch and Valibot dependencies) '@graphql-tools/executor@1.5.3': resolution: {integrity: sha512-mgBFC0bsrZPZLu9EnydpMnAuQ8Iiq0CEbUcsmvXsm2/iYektGHDN/+bmb7hicA6dWZtdPfklYJmr21WD0GnOfA==} @@ -3726,6 +3764,14 @@ packages: resolution: {integrity: sha512-6ZxzVpzDXDa3bJWaHilVayA+BH/1zmxCJoVgvmqJnid/gPoKHxUrS/aC/T6LGQtNHT+XHG9fXPJB4d+IrU30Ew==} hasBin: true + valibot@1.4.2: + resolution: {integrity: sha512-gjdCvJ6d3RyHAneqxMYMW9QMCwYMb3jpOO0IyHZV1bnRHFBHrX3VkIILt5XYR0WhwHiH7Mty8ovuPZ/O3gamrg==} + peerDependencies: + typescript: '>=5' + peerDependenciesMeta: + typescript: + optional: true + verkit@0.3.0: resolution: {integrity: sha512-Njrh4U8UODGajoZ44QS2C/BsoEM9DTI/aCqY5swsizb+/ap0FamvnCMcZAxrR5+aoC0ZqkawEfpC/N2SBc+xeA==} engines: {node: '>=18.12.0'} @@ -4297,6 +4343,7 @@ snapshots: '@fedify/uri-template@2.3.1': {} +<<<<<<< HEAD '@fedify/uri-template@2.4.0-pr.1020.43': {} '@fedify/vocab-runtime@2.4.0-pr.1020.43': @@ -4348,6 +4395,14 @@ snapshots: '@floating-ui/utils': 0.2.12 '@floating-ui/utils@0.2.12': {} +======= + '@formisch/solid@1.0.0(solid-js@1.9.14)(typescript@7.0.2)(valibot@1.4.2(typescript@7.0.2))': + dependencies: + solid-js: 1.9.14 + valibot: 1.4.2(typescript@7.0.2) + optionalDependencies: + typescript: 7.0.2 +>>>>>>> c537fd7 (Add Formisch and Valibot dependencies) '@graphql-tools/executor@1.5.3(graphql@16.14.2)': dependencies: @@ -4731,10 +4786,17 @@ snapshots: dependencies: graphql: 16.14.2 +<<<<<<< HEAD '@pothos/plugin-drizzle@0.17.4(@pothos/core@4.13.0(graphql@16.14.2))(drizzle-orm@1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.21.0)(postgres@3.4.9))(graphql@16.14.2)': dependencies: '@pothos/core': 4.13.0(graphql@16.14.2) drizzle-orm: 1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.21.0)(postgres@3.4.9) +======= + '@pothos/plugin-drizzle@0.17.4(@pothos/core@4.13.0(graphql@16.14.2))(drizzle-orm@1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@types/pg@8.20.0)(pg@8.21.0)(valibot@1.4.2(typescript@7.0.2)))(graphql@16.14.2)': + dependencies: + '@pothos/core': 4.13.0(graphql@16.14.2) + drizzle-orm: 1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@types/pg@8.20.0)(pg@8.21.0)(valibot@1.4.2(typescript@7.0.2)) +>>>>>>> c537fd7 (Add Formisch and Valibot dependencies) graphql: 16.14.2 '@pothos/plugin-errors@4.9.1(@pothos/core@4.13.0(graphql@16.14.2))(graphql@16.14.2)': @@ -5349,6 +5411,7 @@ snapshots: asap@2.0.6: {} +<<<<<<< HEAD asn1js@3.0.10: dependencies: pvtsutils: 1.3.6 @@ -5357,6 +5420,10 @@ snapshots: babel-plugin-jsx-dom-expressions@0.40.7(@babel/core@7.29.7): dependencies: +======= + babel-plugin-jsx-dom-expressions@0.40.7(@babel/core@7.29.7): + dependencies: +>>>>>>> c537fd7 (Add Formisch and Valibot dependencies) '@babel/core': 7.29.7 '@babel/helper-module-imports': 7.18.6 '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) @@ -5522,13 +5589,21 @@ snapshots: get-tsconfig: 4.14.0 jiti: 2.7.0 +<<<<<<< HEAD drizzle-orm@1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.21.0)(postgres@3.4.9): +======= + drizzle-orm@1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@types/pg@8.20.0)(pg@8.21.0)(valibot@1.4.2(typescript@7.0.2)): +>>>>>>> c537fd7 (Add Formisch and Valibot dependencies) optionalDependencies: '@electric-sql/pglite': 0.5.3 '@opentelemetry/api': 1.9.1 '@types/pg': 8.20.0 pg: 8.21.0 +<<<<<<< HEAD postgres: 3.4.9 +======= + valibot: 1.4.2(typescript@7.0.2) +>>>>>>> c537fd7 (Add Formisch and Valibot dependencies) dts-resolver@3.0.0: {} @@ -6568,6 +6643,7 @@ snapshots: seroval: 1.5.4 seroval-plugins: 1.5.4(seroval@1.5.4) +<<<<<<< HEAD solid-presence@0.2.0(solid-js@1.9.14): dependencies: '@corvu/utils': 0.4.2(solid-js@1.9.14) @@ -6578,6 +6654,8 @@ snapshots: '@corvu/utils': 0.4.2(solid-js@1.9.14) solid-js: 1.9.14 +======= +>>>>>>> c537fd7 (Add Formisch and Valibot dependencies) solid-refresh@0.6.3(solid-js@1.9.14): dependencies: '@babel/generator': 7.29.8 @@ -6814,6 +6892,10 @@ snapshots: uuid@14.0.1: {} + valibot@1.4.2(typescript@7.0.2): + optionalDependencies: + typescript: 7.0.2 + verkit@0.3.0: {} vfile-message@4.0.3: From efd7ac3f74d6e934dafc084fee093a20c85e28f0 Mon Sep 17 00:00:00 2001 From: Jiwon Kwon Date: Fri, 4 Sep 2026 20:15:26 +0900 Subject: [PATCH 2/7] Migrate sign-in form to Formisch Replace manual form parsing with a Valibot schema and Formisch-managed field validation while preserving the existing Relay mutation and response handling. I asked Codex to plan and implement the sign-in migration. Codex added schema validation and accessible field errors, preserved the Relay variable and callback behavior, and verified the result with Oxfmt, Oxlint, the web TypeScript check, and a production web build. Assisted-by: Codex:gpt-5.6-sol --- packages/web/src/routes/sign-in.tsx | 70 +++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 13 deletions(-) diff --git a/packages/web/src/routes/sign-in.tsx b/packages/web/src/routes/sign-in.tsx index 8190302..2594e3b 100644 --- a/packages/web/src/routes/sign-in.tsx +++ b/packages/web/src/routes/sign-in.tsx @@ -14,13 +14,18 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . +<<<<<<< HEAD import { Alert } from "@kobalte/core/alert"; import { Button } from "@kobalte/core/button"; import { TextField } from "@kobalte/core/text-field"; +======= +import { Field, Form, type SubmitHandler, createForm } from "@formisch/solid"; +>>>>>>> a7bd83c (Migrate sign-in form to Formisch) import { Title } from "@solidjs/meta"; import { graphql } from "relay-runtime"; import { Match, Show, Switch, createSignal } from "solid-js"; import { createMutation } from "solid-relay"; +import * as v from "valibot"; import type { SignInMutation } from "./__generated__/SignInMutation.graphql.ts"; @@ -34,6 +39,15 @@ const signInMutation = graphql` } `; +const signInSchema = v.object({ + email: v.pipe( + v.string(), + v.trim(), + v.nonEmpty("Enter a valid email address."), + v.email("Enter a valid email address."), + ), +}); + interface SignInResult { message: string; status: "error" | "success"; @@ -43,21 +57,13 @@ export default function SignInPage() { const [result, setResult] = createSignal(); const [commitSignIn, isSigningIn] = createMutation(signInMutation); + const signInForm = createForm({ + schema: signInSchema, + initialInput: { email: "" }, + }); - const submit = (event: SubmitEvent & { currentTarget: HTMLFormElement }) => { - event.preventDefault(); - + const submit: SubmitHandler = ({ email }) => { const verifyUrl = `${globalThis.location.origin}/confirm/{token}?code={code}`; - const formData = new FormData(event.currentTarget); - const email = formData.get("email"); - if (typeof email !== "string" || email === "") { - setResult({ - message: "Enter a valid email address.", - status: "error", - }); - return; - } - setResult(undefined); commitSignIn({ variables: { email, verifyUrl }, @@ -96,6 +102,7 @@ export default function SignInPage() {

Enter your email address to receive a secure sign-in link.

+<<<<<<< HEAD
@@ -113,6 +120,38 @@ export default function SignInPage() { />
+======= + + +>>>>>>> a7bd83c (Migrate sign-in form to Formisch) {(formResult) => ( From ff55cc69ad91207a5baf242d4595368150101e39 Mon Sep 17 00:00:00 2001 From: Jiwon Kwon Date: Fri, 4 Sep 2026 20:41:39 +0900 Subject: [PATCH 3/7] Add semantic form status colors Style error notices in red, success notices in green, and warning notices in orange across light and dark themes. Apply the error treatment to invalid sign-in fields and their inline validation messages. I asked Codex to make login failure, success, and warning states visually distinct. Codex added semantic theme tokens and matching notice, input, focus, and field-error styles, then verified the changes with formatting, linting, TypeScript, and a production web build. Assisted-by: Codex:gpt-5.6-sol --- packages/web/src/app.css | 378 ++++++++++++++++++++++++++++ packages/web/src/routes/sign-in.tsx | 2 +- packages/web/src/styles/drfed.css | 4 + 3 files changed, 383 insertions(+), 1 deletion(-) create mode 100644 packages/web/src/app.css diff --git a/packages/web/src/app.css b/packages/web/src/app.css new file mode 100644 index 0000000..679629b --- /dev/null +++ b/packages/web/src/app.css @@ -0,0 +1,378 @@ +/* +DrFed: A web-based platform for developing and debugging ActivityPub apps +Copyright (C) 2026 DrFed team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU Affero General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU Affero General Public License for more details. + +You should have received a copy of the GNU Affero General Public License +along with this program. If not, see . +*/ + +* { + box-sizing: border-box; +} + +html { + background: var(--bg); + color: var(--ink); + font-family: var(--font-sans); + font-size: var(--text-body); +} + +body { + margin: 0; +} + +button, +input { + font: inherit; +} + +.app-shell { + min-height: 100svh; +} + +.app-header { + background: color-mix(in srgb, var(--bg) 92%, transparent); + border-bottom: 1px solid var(--line); + position: sticky; + top: 0; + z-index: 10; +} + +.app-header-inner { + align-items: center; + display: flex; + height: var(--header-h); + margin: 0 auto; + max-width: var(--container); + padding: 0 var(--gutter); +} + +.brand { + align-items: center; + color: var(--ink); + display: flex; + font-family: var(--font-serif); + font-size: 1.25rem; + font-weight: 700; + gap: 0.65rem; + letter-spacing: var(--tracking-heading); + text-decoration: none; +} + +.brand img { + border-radius: 50%; +} + +.app-nav { + align-self: stretch; + display: flex; + gap: 0.25rem; + margin-left: clamp(1.5rem, 5vw, 4rem); +} + +.app-nav a, +.header-action { + align-items: center; + color: var(--ink-soft); + display: flex; + font-size: 0.9rem; + font-weight: 600; + padding: 0 0.85rem; + position: relative; + text-decoration: none; +} + +.app-nav a:hover, +.header-action:hover, +.app-nav a.is-active, +.header-action.is-active { + color: var(--ink); +} + +.app-nav a.is-active::after { + background: var(--accent); + bottom: -1px; + content: ""; + height: 3px; + left: 0.85rem; + position: absolute; + right: 0.85rem; +} + +.header-action { + border: 1px solid var(--line-strong); + border-radius: var(--radius-pill); + margin-left: auto; + min-height: 2.4rem; +} + +.header-action.is-active { + background: var(--accent-tint); + border-color: var(--accent-soft); +} + +.app-header a:focus-visible { + outline: 3px solid var(--accent-soft); + outline-offset: 0.2rem; +} + +.app-content { + margin: 0 auto; + max-width: var(--container); + min-height: calc(100svh - var(--header-h)); + padding: clamp(2rem, 5vw, 4rem) var(--gutter); +} + +.app-content > main:not(.form-page) { + width: 100%; +} + +.app-content > main:not(.form-page) :is(h1, h2, p) { + margin-top: 0; +} + +.app-content h1, +.app-content h2 { + font-family: var(--font-serif); + letter-spacing: var(--tracking-heading); +} + +.app-content h1 { + font-size: var(--text-heading-2); + line-height: var(--leading-heading); +} + +.app-content p { + color: var(--ink-soft); + line-height: var(--leading-body); +} + +.form-page { + align-items: center; + display: flex; + justify-content: center; + min-height: calc(100svh - var(--header-h) - clamp(4rem, 10vw, 8rem)); +} + +.panel { + background: var(--card); + border: 1px solid var(--line); + border-radius: var(--radius); + box-shadow: var(--shadow-sm); +} + +.form-panel { + max-width: 28rem; + padding: clamp(1.5rem, 4vw, 2rem); + width: 100%; +} + +.panel-header { + border-bottom: 1px solid var(--line); + margin: 0 calc(clamp(1.5rem, 4vw, 2rem) * -1) 1.5rem; + padding: 0 clamp(1.5rem, 4vw, 2rem) 1.5rem; +} + +.panel-header h1 { + font-size: 1.65rem; + margin: 0 0 0.5rem; +} + +.panel-header p { + font-size: 0.95rem; + margin: 0; +} + +.form-panel form { + display: grid; + gap: 1rem; +} + +.field { + display: grid; + gap: 0.5rem; +} + +.field-heading { + align-items: baseline; + display: flex; + font-size: 0.9rem; + font-weight: 650; + justify-content: space-between; +} + +.field-status { + color: var(--ink-faint); + font-family: var(--font-mono); + font-size: 0.68rem; + font-weight: 500; + letter-spacing: 0.04em; + text-transform: uppercase; +} + +.field-status.required { + color: var(--accent-strong); +} + +.field input { + background: var(--surface-2); + border: 1px solid var(--line-strong); + border-radius: var(--radius-sm); + color: var(--ink); + min-width: 0; + padding: 0.9rem 1rem; + transition: + border-color var(--duration-fast) var(--ease-standard), + box-shadow var(--duration-fast) var(--ease-standard); +} + +.field input::placeholder { + color: var(--ink-faint); +} + +.field input:focus { + border-color: var(--accent); + box-shadow: 0 0 0 3px var(--accent-tint); + outline: none; +} + +.field input:read-only { + border-style: dashed; + color: var(--ink-soft); + cursor: default; + font-family: var(--font-mono); +} + +.field-hint { + color: var(--ink-faint); + font-size: 0.78rem; + line-height: 1.45; + margin: 0; +} + +.field-error { + color: var(--danger); + font-size: 0.78rem; + line-height: 1.45; + margin: 0; +} + +.field input[aria-invalid="true"] { + border-color: var(--danger); +} + +.field input[aria-invalid="true"]:focus { + border-color: var(--danger); + box-shadow: 0 0 0 3px color-mix(in srgb, var(--danger) 20%, transparent); +} + +.button { + align-items: center; + border: 0; + border-radius: var(--radius-sm); + cursor: pointer; + display: inline-flex; + font-weight: 700; + justify-content: center; + min-height: 2.9rem; + padding: 0.75rem 1.1rem; +} + +.button:focus-visible { + outline: 3px solid var(--accent-soft); + outline-offset: 0.2rem; +} + +.button.primary { + background: var(--accent); + color: var(--on-accent); + transition: + background var(--duration-fast) var(--ease-standard), + transform var(--duration-fast) var(--ease-standard); +} + +.button.primary:hover:not(:disabled) { + background: var(--accent-strong); +} + +.button:disabled { + cursor: wait; + opacity: 0.65; +} + +.notice { + background: var(--accent-tint); + border-radius: var(--radius-sm); + color: var(--ink); + line-height: 1.5; + margin: 1.25rem 0 0; + padding: 0.85rem 1rem; +} + +.notice.error { + background: color-mix(in srgb, var(--danger) 12%, var(--surface)); + border-left: 3px solid var(--danger); + color: var(--danger); +} + +.notice.success { + background: color-mix(in srgb, var(--success) 12%, var(--surface)); + border-left: 3px solid var(--success); + color: var(--success); +} + +.notice.warning { + background: color-mix(in srgb, var(--warning) 12%, var(--surface)); + border-left: 3px solid var(--warning); + color: var(--warning); +} + +@media (max-width: 600px) { + .app-header-inner { + padding: 0 1rem; + } + + .brand span { + display: none; + } + + .app-nav { + margin-left: 0.75rem; + } + + .app-nav a, + .header-action { + padding-inline: 0.65rem; + } + + .app-nav a.is-active::after { + left: 0.65rem; + right: 0.65rem; + } + + .app-content { + padding: 1.5rem 1rem 3rem; + } + + .form-page { + align-items: flex-start; + min-height: auto; + } +} + +@media (prefers-reduced-motion: reduce) { + .button.primary, + .field input { + transition: none; + } +} diff --git a/packages/web/src/routes/sign-in.tsx b/packages/web/src/routes/sign-in.tsx index 2594e3b..967b925 100644 --- a/packages/web/src/routes/sign-in.tsx +++ b/packages/web/src/routes/sign-in.tsx @@ -142,7 +142,7 @@ export default function SignInPage() { /> {(errors) => ( - + {errors()[0]} )} diff --git a/packages/web/src/styles/drfed.css b/packages/web/src/styles/drfed.css index b306ab6..6301508 100644 --- a/packages/web/src/styles/drfed.css +++ b/packages/web/src/styles/drfed.css @@ -56,6 +56,8 @@ along with this program. If not, see . --line: #e9e5e1; --line-strong: #dad4cf; --danger: #cf3f43; + --success: #247a46; + --warning: #9a5800; /* Elevation */ --shadow-sm: 0 1px 2px rgb(38 33 31 / 5%), 0 2px 8px rgb(38 33 31 / 4%); @@ -100,6 +102,8 @@ along with this program. If not, see . --line: #332f2a; --line-strong: #453f39; --danger: #ef726b; + --success: #66cf8a; + --warning: #f0ad4e; --shadow-sm: 0 1px 2px rgb(0 0 0 / 30%); --shadow-md: 0 6px 24px rgb(0 0 0 / 40%); From 209ca7e6d3ca3be183c3eec72f862897781c8ffd Mon Sep 17 00:00:00 2001 From: Jiwon Kwon Date: Sat, 5 Sep 2026 18:23:00 +0900 Subject: [PATCH 4/7] Migrate instance creation form to Formisch Manage the generated slug with Formisch and Valibot while preserving Relay mutation handling. Keep the submit button inside the form and associate the read-only hint and validation error with the slug field. I chose to migrate the read-only form for consistency with sign-in. Codex reviewed the migration and accessibility feedback, identified the required form and description associations, formatted the file, and verified it with Oxlint and the web TypeScript check. Assisted-by: Codex:gpt-5.6-sol --- .../src/routes/workspace/create/instance.tsx | 87 ++++++++++++------- 1 file changed, 56 insertions(+), 31 deletions(-) diff --git a/packages/web/src/routes/workspace/create/instance.tsx b/packages/web/src/routes/workspace/create/instance.tsx index 7063487..f6235bb 100644 --- a/packages/web/src/routes/workspace/create/instance.tsx +++ b/packages/web/src/routes/workspace/create/instance.tsx @@ -17,11 +17,13 @@ import { faker } from "@faker-js/faker"; import { Button } from "@kobalte/core/button"; import { TextField } from "@kobalte/core/text-field"; +import { Field, Form, type SubmitHandler, createForm } from "@formisch/solid"; import { Title } from "@solidjs/meta"; import { useNavigate } from "@solidjs/router"; import { graphql } from "relay-runtime"; import { Show, createSignal } from "solid-js"; import { createMutation } from "solid-relay"; +import * as v from "valibot"; import type { CreateInstanceMutation } from "./__generated__/CreateInstanceMutation.graphql.ts"; @@ -41,22 +43,24 @@ const createInstanceMutation = graphql` } `; +const createInstanceSchema = v.object({ + slug: v.pipe(v.string(), v.trim(), v.nonEmpty("Enter a valid slug.")), +}); + export default function CreateInstancePage() { const navigate = useNavigate(); const [errorMessage, setErrorMessage] = createSignal(); const [commitCreateInstance, isCreatingInstance] = createMutation(createInstanceMutation); - const submit = (event: SubmitEvent & { currentTarget: HTMLFormElement }) => { - event.preventDefault(); - - const formData = new FormData(event.currentTarget); - const slug = formData.get("slug"); - if (typeof slug !== "string" || slug === "") { - setErrorMessage("Enter a valid slug."); - return; - } + const createInstanceForm = createForm({ + schema: createInstanceSchema, + initialInput: { + slug: `${faker.word.noun()}-${faker.word.noun()}-${faker.word.noun()}`, + }, + }); + const submit: SubmitHandler = ({ slug }) => { setErrorMessage(undefined); commitCreateInstance({ variables: { slug }, @@ -110,26 +114,48 @@ export default function CreateInstancePage() {

Review the generated identifier for your new instance.

-
- - - Slug - Generated · Read only - - - - - - DrFed generates this identifier automatically. It cannot be - edited. - - - + + + {(field) => ( + + + Slug + Generated · Read only + + + + + + DrFed generates this identifier automatically. It cannot be + edited. + + + + {(errors) => ( + + {errors()[0]} + + )} + + + )} + -
- +

Enter your email address to receive a secure sign-in link.

-<<<<<<< HEAD -
- - - Email address - - Required - - - - - -
-======= - ->>>>>>> a7bd83c (Migrate sign-in form to Formisch) {(formResult) => ( diff --git a/packages/web/src/routes/workspace/create/instance.tsx b/packages/web/src/routes/workspace/create/instance.tsx index b2a2131..ca36f90 100644 --- a/packages/web/src/routes/workspace/create/instance.tsx +++ b/packages/web/src/routes/workspace/create/instance.tsx @@ -15,9 +15,9 @@ // along with this program. If not, see . import { faker } from "@faker-js/faker"; +import { Field, Form, type SubmitHandler, createForm } from "@formisch/solid"; import { Button } from "@kobalte/core/button"; import { TextField } from "@kobalte/core/text-field"; -import { Field, Form, type SubmitHandler, createForm } from "@formisch/solid"; import { Title } from "@solidjs/meta"; import { useNavigate } from "@solidjs/router"; import { graphql } from "relay-runtime"; From 7d64794419c4cc8a0c203acb44c37f04479d7907 Mon Sep 17 00:00:00 2001 From: Jiwon Kwon Date: Sun, 6 Sep 2026 16:20:01 +0900 Subject: [PATCH 7/7] Fix dependency conflicts --- packages/web/package.json | 3 -- pnpm-lock.yaml | 70 +++++++-------------------------------- 2 files changed, 12 insertions(+), 61 deletions(-) diff --git a/packages/web/package.json b/packages/web/package.json index e111fce..32c3936 100644 --- a/packages/web/package.json +++ b/packages/web/package.json @@ -46,11 +46,8 @@ "preview": "vite preview" }, "dependencies": { -<<<<<<< HEAD "@kobalte/core": "^0.13.13", -======= "@formisch/solid": "1.0.0", ->>>>>>> c537fd7 (Add Formisch and Valibot dependencies) "@solidjs/meta": "^0.29.4", "@solidjs/router": "^1.0.0", "@solidjs/start": "^2.0.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4ac3b38..6380d0b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -130,11 +130,7 @@ importers: version: 0.6.0-dev.263(@upyo/core@0.6.0-dev.263) drizzle-orm: specifier: 'catalog:' -<<<<<<< HEAD - version: 1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.21.0)(postgres@3.4.9) -======= - version: 1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@types/pg@8.20.0)(pg@8.21.0)(valibot@1.4.2(typescript@7.0.2)) ->>>>>>> c537fd7 (Add Formisch and Valibot dependencies) + version: 1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.21.0)(postgres@3.4.9)(valibot@1.4.2(typescript@7.0.2)) graphql: specifier: 'catalog:' version: 16.14.2 @@ -183,11 +179,7 @@ importers: version: 4.13.0(graphql@16.14.2) '@pothos/plugin-drizzle': specifier: ^0.17.4 -<<<<<<< HEAD - version: 0.17.4(@pothos/core@4.13.0(graphql@16.14.2))(drizzle-orm@1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.21.0)(postgres@3.4.9))(graphql@16.14.2) -======= - version: 0.17.4(@pothos/core@4.13.0(graphql@16.14.2))(drizzle-orm@1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@types/pg@8.20.0)(pg@8.21.0)(valibot@1.4.2(typescript@7.0.2)))(graphql@16.14.2) ->>>>>>> c537fd7 (Add Formisch and Valibot dependencies) + version: 0.17.4(@pothos/core@4.13.0(graphql@16.14.2))(drizzle-orm@1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.21.0)(postgres@3.4.9)(valibot@1.4.2(typescript@7.0.2)))(graphql@16.14.2) '@pothos/plugin-errors': specifier: ^4.9.1 version: 4.9.1(@pothos/core@4.13.0(graphql@16.14.2))(graphql@16.14.2) @@ -205,11 +197,7 @@ importers: version: 0.6.0-dev.263(@upyo/core@0.6.0-dev.263) drizzle-orm: specifier: 'catalog:' -<<<<<<< HEAD - version: 1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.21.0)(postgres@3.4.9) -======= - version: 1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@types/pg@8.20.0)(pg@8.21.0)(valibot@1.4.2(typescript@7.0.2)) ->>>>>>> c537fd7 (Add Formisch and Valibot dependencies) + version: 1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.21.0)(postgres@3.4.9)(valibot@1.4.2(typescript@7.0.2)) graphql: specifier: 'catalog:' version: 16.14.2 @@ -249,13 +237,8 @@ importers: version: 2.3.0-dev.840 drizzle-orm: specifier: 'catalog:' -<<<<<<< HEAD - version: 1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.21.0)(postgres@3.4.9) + version: 1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.21.0)(postgres@3.4.9)(valibot@1.4.2(typescript@7.0.2)) postgres: -======= - version: 1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@types/pg@8.20.0)(pg@8.21.0)(valibot@1.4.2(typescript@7.0.2)) - pg: ->>>>>>> c537fd7 (Add Formisch and Valibot dependencies) specifier: 'catalog:' version: 3.4.9 devDependencies: @@ -277,15 +260,12 @@ importers: packages/web: dependencies: -<<<<<<< HEAD - '@kobalte/core': - specifier: ^0.13.13 - version: 0.13.13(solid-js@1.9.14) -======= '@formisch/solid': specifier: 1.0.0 version: 1.0.0(solid-js@1.9.14)(typescript@7.0.2)(valibot@1.4.2(typescript@7.0.2)) ->>>>>>> c537fd7 (Add Formisch and Valibot dependencies) + '@kobalte/core': + specifier: ^0.13.13 + version: 0.13.13(solid-js@1.9.14) '@solidjs/meta': specifier: ^0.29.4 version: 0.29.4(solid-js@1.9.14) @@ -856,7 +836,6 @@ packages: resolution: {integrity: sha512-322xch1WhasP/vjH4yDPA1RnYwI6tlG72aH7fCpdFge8IC845urKEMET9LzJ2G5HfeCKAXPAcaZZEx9FXnTNrg==} engines: {bun: '>=1.1.0', deno: '>=2.0.0', node: '>=22.0.0'} -<<<<<<< HEAD '@fedify/uri-template@2.4.0-pr.1020.43': resolution: {integrity: sha512-2JsU5q+pDNMuoD3ZcJLwubpZWgixwcy0H4yVY/bpz/vEEEWu+g22pa6r52rloWvn5ARJXAkMxcyHDcBlZojpBg==} engines: {bun: '>=1.1.0', deno: '>=2.0.0', node: '>=22.0.0'} @@ -885,7 +864,7 @@ packages: '@floating-ui/utils@0.2.12': resolution: {integrity: sha512-HpCo8tmWzLVad5s2d19EhAz5zqrrQ6s69qd6moPMQvkOuSwDT1YgRfWSVuc4ennqrgv3OHppiOGMQ7oC13yIww==} -======= + '@formisch/solid@1.0.0': resolution: {integrity: sha512-J5RF48YCM6SNYVkt+CJ0+5MHPC38wi3zgBM5hOpwnWFU5Q/gtdjb+vO63uJKWLAU7XQlnZVDo7rBzf8bVsvvcA==} peerDependencies: @@ -895,7 +874,6 @@ packages: peerDependenciesMeta: typescript: optional: true ->>>>>>> c537fd7 (Add Formisch and Valibot dependencies) '@graphql-tools/executor@1.5.3': resolution: {integrity: sha512-mgBFC0bsrZPZLu9EnydpMnAuQ8Iiq0CEbUcsmvXsm2/iYektGHDN/+bmb7hicA6dWZtdPfklYJmr21WD0GnOfA==} @@ -4343,7 +4321,6 @@ snapshots: '@fedify/uri-template@2.3.1': {} -<<<<<<< HEAD '@fedify/uri-template@2.4.0-pr.1020.43': {} '@fedify/vocab-runtime@2.4.0-pr.1020.43': @@ -4395,14 +4372,13 @@ snapshots: '@floating-ui/utils': 0.2.12 '@floating-ui/utils@0.2.12': {} -======= + '@formisch/solid@1.0.0(solid-js@1.9.14)(typescript@7.0.2)(valibot@1.4.2(typescript@7.0.2))': dependencies: solid-js: 1.9.14 valibot: 1.4.2(typescript@7.0.2) optionalDependencies: typescript: 7.0.2 ->>>>>>> c537fd7 (Add Formisch and Valibot dependencies) '@graphql-tools/executor@1.5.3(graphql@16.14.2)': dependencies: @@ -4786,17 +4762,10 @@ snapshots: dependencies: graphql: 16.14.2 -<<<<<<< HEAD - '@pothos/plugin-drizzle@0.17.4(@pothos/core@4.13.0(graphql@16.14.2))(drizzle-orm@1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.21.0)(postgres@3.4.9))(graphql@16.14.2)': - dependencies: - '@pothos/core': 4.13.0(graphql@16.14.2) - drizzle-orm: 1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.21.0)(postgres@3.4.9) -======= - '@pothos/plugin-drizzle@0.17.4(@pothos/core@4.13.0(graphql@16.14.2))(drizzle-orm@1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@types/pg@8.20.0)(pg@8.21.0)(valibot@1.4.2(typescript@7.0.2)))(graphql@16.14.2)': + '@pothos/plugin-drizzle@0.17.4(@pothos/core@4.13.0(graphql@16.14.2))(drizzle-orm@1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.21.0)(postgres@3.4.9)(valibot@1.4.2(typescript@7.0.2)))(graphql@16.14.2)': dependencies: '@pothos/core': 4.13.0(graphql@16.14.2) - drizzle-orm: 1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@types/pg@8.20.0)(pg@8.21.0)(valibot@1.4.2(typescript@7.0.2)) ->>>>>>> c537fd7 (Add Formisch and Valibot dependencies) + drizzle-orm: 1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.21.0)(postgres@3.4.9)(valibot@1.4.2(typescript@7.0.2)) graphql: 16.14.2 '@pothos/plugin-errors@4.9.1(@pothos/core@4.13.0(graphql@16.14.2))(graphql@16.14.2)': @@ -5411,7 +5380,6 @@ snapshots: asap@2.0.6: {} -<<<<<<< HEAD asn1js@3.0.10: dependencies: pvtsutils: 1.3.6 @@ -5420,10 +5388,6 @@ snapshots: babel-plugin-jsx-dom-expressions@0.40.7(@babel/core@7.29.7): dependencies: -======= - babel-plugin-jsx-dom-expressions@0.40.7(@babel/core@7.29.7): - dependencies: ->>>>>>> c537fd7 (Add Formisch and Valibot dependencies) '@babel/core': 7.29.7 '@babel/helper-module-imports': 7.18.6 '@babel/plugin-syntax-jsx': 7.29.7(@babel/core@7.29.7) @@ -5589,21 +5553,14 @@ snapshots: get-tsconfig: 4.14.0 jiti: 2.7.0 -<<<<<<< HEAD - drizzle-orm@1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.21.0)(postgres@3.4.9): -======= - drizzle-orm@1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@types/pg@8.20.0)(pg@8.21.0)(valibot@1.4.2(typescript@7.0.2)): ->>>>>>> c537fd7 (Add Formisch and Valibot dependencies) + drizzle-orm@1.0.0-beta.22(@electric-sql/pglite@0.5.3)(@opentelemetry/api@1.9.1)(@types/pg@8.20.0)(pg@8.21.0)(postgres@3.4.9)(valibot@1.4.2(typescript@7.0.2)): optionalDependencies: '@electric-sql/pglite': 0.5.3 '@opentelemetry/api': 1.9.1 '@types/pg': 8.20.0 pg: 8.21.0 -<<<<<<< HEAD postgres: 3.4.9 -======= valibot: 1.4.2(typescript@7.0.2) ->>>>>>> c537fd7 (Add Formisch and Valibot dependencies) dts-resolver@3.0.0: {} @@ -6643,7 +6600,6 @@ snapshots: seroval: 1.5.4 seroval-plugins: 1.5.4(seroval@1.5.4) -<<<<<<< HEAD solid-presence@0.2.0(solid-js@1.9.14): dependencies: '@corvu/utils': 0.4.2(solid-js@1.9.14) @@ -6654,8 +6610,6 @@ snapshots: '@corvu/utils': 0.4.2(solid-js@1.9.14) solid-js: 1.9.14 -======= ->>>>>>> c537fd7 (Add Formisch and Valibot dependencies) solid-refresh@0.6.3(solid-js@1.9.14): dependencies: '@babel/generator': 7.29.8