Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
215 commits
Select commit Hold shift + click to select a range
a595966
[P059] feat: scaffold pdf-form-copilot example app
bendersej Apr 22, 2026
4004cca
[P059] chore: point pdf-form-copilot at /demo/ CDN paths
bendersej Apr 22, 2026
55135a4
[P059] feat: iframe bridge + debug panel (Phase 2)
bendersej Apr 22, 2026
5dde22d
[P059] feat: chat + Anthropic proxy + rate limit (Phase 3)
bendersej Apr 22, 2026
ce618df
[P059] feat: use-case switcher, language picker, richer info modal
bendersej Apr 22, 2026
4bc5429
[P059] feat: persist language in URL + restore suggested prompts
bendersej Apr 22, 2026
4a3051d
[P059] chore: use SimplePDF favicon
bendersej Apr 22, 2026
96e33bf
[P059] feat: info modal footer with Pro plan, source link, star button
bendersej Apr 22, 2026
3690ffd
[P059] fix: compact tool results + move Pro/GitHub banner to top
bendersej Apr 22, 2026
9bfcf40
[P059] feat: summarizer sub-agent for get_document_content
bendersej Apr 22, 2026
e5a92f1
[P059] feat: widen info modal to 4-card grid + add scanned PDF use case
bendersej Apr 22, 2026
55e3ec0
[P059] feat: enable Anthropic prompt caching on system prompt + tools
bendersej Apr 22, 2026
474ae5d
[P059] feat: make rate limits env-driven (RATE_LIMIT_PER_HOUR / _PER_…
bendersej Apr 22, 2026
aceaacb
[P059] feat: dev-only POST /api/rate-limit-reset to wipe the bucket
bendersej Apr 22, 2026
559e918
[P059] feat: system-prompt flow, chat toolbar, field polling, clickab…
bendersej Apr 22, 2026
cd0d35c
[P059] chore: rewrite system prompt for non-technical UX + fill-first…
bendersej Apr 22, 2026
f491c8a
[P059] chore: system prompt bans process-narration + batch questions
bendersej Apr 22, 2026
6ca6861
[P059] perf: disable summarizer by default + timing logs
bendersej Apr 22, 2026
97f85dd
[P059] chore: harden system prompt — no narration, auto-advance fields
bendersej Apr 22, 2026
41819d4
[P059] chore: system prompt — text-only-when-asking rule + focus_fiel…
bendersej Apr 22, 2026
93f3930
[P059] perf: fail fast on Anthropic rate limits + thinking indicator
bendersej Apr 22, 2026
3eacc8c
[P059] fix: correct checkbox values + require tool-error handling
bendersej Apr 22, 2026
b0fb856
[P059] perf: rolling cache breakpoint on the latest user turn
bendersej Apr 22, 2026
d639d44
[P059] perf: tighter get_document_content cap (400 chars x 4 pages)
bendersej Apr 22, 2026
8cf1706
[P059] chore: open editor iframe with ignoreExistingFields=true
bendersej Apr 22, 2026
d934fa7
[P059] perf: get_document_content returns only the first page
bendersej Apr 22, 2026
8db4d87
[P059] chore: raise default rate limits to 20/hr + 100/day
bendersej Apr 22, 2026
855c5e2
[P059] feat: assistant questions rendered bold + blue
bendersej Apr 22, 2026
637e28f
[P059] fix: rate-limit only fresh user turns, raise to 30/hr + 150/day
bendersej Apr 22, 2026
0b1c208
[P059] chore: switch iframe subdomain to pdf-form-copilot.simplepdf.com
bendersej Apr 22, 2026
cd04015
[P059] fix: 4s optimistic EDITOR_READY fallback + message logging
bendersej Apr 22, 2026
f52edf8
[P059] chore: drop 'Which fields are still empty?' suggestion
bendersej Apr 22, 2026
b7bfc03
[P059] feat: route every user-facing string through react-i18next (en…
bendersej Apr 22, 2026
1531584
[P059] feat: language picker drives UI + forms are locale-keyed
bendersej Apr 22, 2026
27087d6
[P059] feat: shimmer 'Thinking…' indicator with brain icon
bendersej Apr 22, 2026
d256c2b
[P059] feat: read / write icons on tool-invocation cards
bendersej Apr 22, 2026
30b9eb3
[P059] chore: humanize tool-invocation labels
bendersej Apr 22, 2026
497645f
[P059] feat: polished tool cards + brand blue
bendersej Apr 22, 2026
6894a4b
[P059] chore: tool-card spacing + brand green/red
bendersej Apr 22, 2026
3815a8e
[P059] chore: align palette with client/styles/variables.scss + tight…
bendersej Apr 22, 2026
45264fa
[P059] feat: collapse consecutive tool calls into 'N actions' block
bendersej Apr 22, 2026
7202766
[P059] feat: group summary shows a single kind-aware icon next to count
bendersej Apr 22, 2026
7e74bde
[P059] chore: neutral group icon + auto-expand on failure
bendersej Apr 22, 2026
84a447f
[P059] chore: assistant bubbles min-w 296px so short answers don't lo…
bendersej Apr 22, 2026
eb912e7
[P059] chore: verbose client logs for tool dispatch
bendersej Apr 22, 2026
4b2a108
[P059] chore: system prompt demands write-before-ask (no batching)
bendersej Apr 22, 2026
16fcc75
[P059] chore: suggested-prompt buttons use brand blue primary
bendersej Apr 22, 2026
1f130e1
[P059] chore: only primary suggested prompt uses filled brand blue
bendersej Apr 22, 2026
137bd17
[P059] chore: suggested prompts side-by-side with smaller text
bendersej Apr 22, 2026
e51ed93
[P059] chore: humanize second suggested prompt
bendersej Apr 22, 2026
08f0b07
[P059] chore: system prompt handles user hesitancy by handing off the…
bendersej Apr 22, 2026
ffc87dd
[P059] chore: secondary prompt hover uses brand blue border only
bendersej Apr 22, 2026
7969e9f
[P059] chore: cursor-pointer on interactive buttons
bendersej Apr 22, 2026
7a84c2b
[P059] chore: toolbar hover/active match secondary-button pattern
bendersej Apr 22, 2026
26aeb60
[P059] chore: system prompt — always focus_field before set_field_value
bendersej Apr 22, 2026
ce78d34
[P059] feat: 'About SimplePDF' section in info modal
bendersej Apr 22, 2026
d2e1be8
[P059] chore: rename header dropdown label 'Use case' → 'Loaded PDF'
bendersej Apr 22, 2026
52b4f41
[P059] feat: replace native form select with a custom FormPicker
bendersej Apr 22, 2026
fc00331
[P059] feat: 'Pick your own PDF' option in the Loaded PDF dropdown
bendersej Apr 22, 2026
8c2c408
[P059] chore: move 'Pick your own PDF' to the top of the dropdown
bendersej Apr 22, 2026
663968d
[P059] feat: gate chat on DOCUMENT_LOADED + hard reset on form switch
bendersej Apr 22, 2026
c63626a
[P059] feat: iframe company identifier via VITE_SIMPLEPDF_COMPANY_IDE…
bendersej Apr 22, 2026
4cd21b7
[P059] feat: chat subtitle reads 'Load a document' for custom form + …
bendersej Apr 22, 2026
31bc536
[P059] chore: privacy note under 'Pick your own PDF' row in the dropdown
bendersej Apr 22, 2026
968b4f7
[P059] chore: pluralize privacy note copy
bendersej Apr 22, 2026
8133d8c
[P059] chore: 'Pick your own PDF' subtitle → 'Any PDF works'
bendersej Apr 22, 2026
c255c54
[P059] chore: refocus chat input whenever it's the user's turn
bendersej Apr 22, 2026
8c327b9
[P059] fix: fallback timer must not reset isDocumentLoaded
bendersej Apr 22, 2026
a48b830
[P059] chore: SimplePDF logo next to brand in header
bendersej Apr 22, 2026
3115df5
[P059] chore: tighter gap between header logo and brand
bendersej Apr 22, 2026
c7a5562
[P059] chore: logo + brand are a link to simplepdf.com?s=form-copilot
bendersej Apr 22, 2026
543711d
[P059] feat: friendly error banner (debug-only raw details) + gracefu…
bendersej Apr 22, 2026
cf09e12
[P059] chore: pin chat input border to 1px + brand-blue focus
bendersej Apr 22, 2026
e5c93d1
[P059] chore: slim info-modal banner, hoist GitHub link + star to the…
bendersej Apr 22, 2026
323d0f0
[P059] chore: 'Powered by the SimplePDF Pro plan' moves to modal footer
bendersej Apr 22, 2026
62ddfab
[P059] chore: full 'SimplePDF Pro plan' string rendered in brand blue
bendersej Apr 22, 2026
dde2314
[P059] chore: remove 'Replies will be in <Language>' banner
bendersej Apr 22, 2026
113bb15
[P059] feat: Switch AI model modal scaffold (UI only, no routing yet)
bendersej Apr 22, 2026
e282e33
[P059] feat: BYOK end-to-end for Anthropic / OpenAI / Mistral
bendersej Apr 22, 2026
bb44d7a
[P059] chore: switch BYOK 'Coming soon' slots to enterprise providers
bendersej Apr 22, 2026
4afa6ba
[P059] feat: active editor-ready probe + interest capture for 'Coming…
bendersej Apr 22, 2026
0407bcd
[P059] chore: hide 'Coming soon' pill until the coming-soon row is cl…
bendersej Apr 22, 2026
cbb8982
[P059] feat: lifetime per-IP rate limit (75 turns, no window rollover)
bendersej Apr 22, 2026
f824af1
[P059] feat: origin allow-list + optional S3-persisted rate limit
bendersej Apr 22, 2026
9662070
[P059] chore: address /code-review findings on the server side
bendersej Apr 22, 2026
6f2809f
[P059] feat: product polish pass on Form Copilot demo
bendersej Apr 23, 2026
1ca16ea
[P059] feat: polish pass #2 (responsiveness, i18n, error surface, gen…
bendersej Apr 23, 2026
34238f9
[P059] feat: rate-limit cap 75->10, SHARED_API_KEYS invite-link path,…
bendersej Apr 23, 2026
4ea9928
[P059] feat: expand EN form catalogue + locale-aware routing
bendersej Apr 23, 2026
813e8af
[P059] feat: Cerfa d'Or easter egg (French locale only)
bendersej Apr 23, 2026
dc3a424
[P059] chore: require RATE_LIMIT_LIFETIME, drop origin allow-list, po…
bendersej Apr 23, 2026
8ab5662
[P059] feat: SHARED_API_KEYS carries per-share lifetime; rate limit i…
bendersej Apr 23, 2026
71fc0a4
[P059] feat: MNDA picker subtext annotates '(scanned document)'
bendersej Apr 23, 2026
8b470e0
[P059] feat: add CERFA 14598 (rupture conventionnelle) to FR form cat…
bendersej Apr 23, 2026
2e9b91f
[P059] refactor: shared http helpers, default path unlimited, drop de…
bendersej Apr 23, 2026
9e85c6c
[P059] refactor: remove `as` casts, merge field polls, drop ModelPick…
bendersej Apr 23, 2026
dbb929e
[P059] refactor: bridge state machine + chatHistoryStore LRU + ternar…
bendersej Apr 23, 2026
baa3e1a
[P059] refactor: locale-aware simplepdf.com links + dead-code prune +…
bendersej Apr 23, 2026
e66b0e8
[P059] feat: Cerfa d'Or tweet copy focuses on AI + @simple_pdf handle
bendersej Apr 23, 2026
07de448
[P059] refactor: shared_keys uses memoized getConfig() instead of mod…
bendersej Apr 23, 2026
30cac10
[P059] refactor: ModalHeader primitive, ErrorBanner extracted, tool-r…
bendersej Apr 23, 2026
dfbbaad
[P059] docs: .env.example shape update (SHARED_API_KEYS, drop stale v…
bendersej Apr 23, 2026
50a7088
[P059] feat: IP_HASH_SALT required when S3 persistence is enabled
bendersej Apr 23, 2026
07282fa
[P059] feat: same-origin gate + invite-only (drop ANTHROPIC_API_KEY f…
bendersej Apr 23, 2026
a6cfbaf
[P059] chore: BYOK lifts doc truncation, biome config + README refresh
bendersej Apr 23, 2026
f3111e3
[P059] feat: update welcome-banner copy + add 'How Form Copilot works…
bendersej Apr 23, 2026
c5424bc
[P059] feat: share=id consumed into httpOnly cookie + Model Picker co…
bendersej Apr 23, 2026
33678b6
[P059] chore: code-review round 2 remediation + fail-closed rate limit
bendersej Apr 23, 2026
fd43452
[P059] feat: VITE_SIMPLEPDF_BASE_DOMAIN env override for the editor host
bendersej Apr 23, 2026
36d59f5
[P059] feat: stop GET_FIELDS polling once hasFilledField flips true
bendersej Apr 23, 2026
3facc64
[P059] refactor: typed monitoring interface + DEV-only log gate
bendersej Apr 23, 2026
eafd1c4
[P059] fix: restore chat auto-scroll via use-stick-to-bottom
bendersej Apr 23, 2026
1a2f92e
[P059] fix: restore h-full centering inside use-stick-to-bottom wrapper
bendersej Apr 23, 2026
838eefa
[P059] refactor: split iframe bridge + tool calling into self-contain…
bendersej Apr 24, 2026
9a8e04d
[P059] refactor: code-review remediation + expose BOXED_TEXT toolbar …
bendersej Apr 24, 2026
e8f5557
[P059] refactor: folder-organize byok + error-classifier + rename ada…
bendersej Apr 24, 2026
86171fd
[P059] refactor: move embed-bridge-adapters/ under src/lib/
bendersej Apr 24, 2026
70361cd
[P059] feat: reorder toolbar + custom SVG icon for boxed text
bendersej Apr 24, 2026
14a4d1e
[P059] fix: code-review remediation (BOXED_TEXT drift + i18n) + local…
bendersej Apr 24, 2026
ef04fa7
[P059] refactor: move Dropdown + Modal into components/ui
bendersej Apr 24, 2026
154a5bb
[P059] fix: harden chat-store hydrate/write ordering + U+001F cache-k…
bendersej Apr 24, 2026
358076e
[P059] refactor: cache-key delimiter ':' (UUID + ISO locale can't col…
bendersej Apr 24, 2026
53e6a62
[P059] chore: drop default scaffolded robots.txt
bendersej Apr 24, 2026
a715c9c
[P059] fix: bridge timeouts + error envelope surfacing + raw traffic …
bendersej Apr 24, 2026
3b21500
[P059] fix: accept editor void-success shape in bridge result guard
bendersej Apr 24, 2026
69b46e5
[P059] chore: always-on bridge error sink + promote bridge failure logs
bendersej Apr 24, 2026
aec1b82
[P059] refactor: centralize log gating in monitoring.ts
bendersej Apr 24, 2026
7472bfd
[P059] refactor: carry ?share=<id> on the URL (drop the cookie round-…
bendersej Apr 24, 2026
eb485f0
[P059] feat: demo-model union drives chat header + server dispatch
bendersej Apr 24, 2026
46dba6a
[P059] refactor: prefix DemoModel enum with provider name
bendersej Apr 24, 2026
617ea4d
[P059] chore: shorten DEMO_MODELS labels
bendersej Apr 24, 2026
6fdf8be
[P059] chore: restore 'Claude Haiku 4.5' label (brand name in the UI)
bendersej Apr 24, 2026
3d5960e
[P059] chore: strip Recommended section + rename model picker header
bendersej Apr 24, 2026
067a708
[P059] feat: restore Currently used section in model picker (demo + B…
bendersej Apr 24, 2026
0de6d0b
[P059] fix: map upstream demo-key 4xx to demo_rate_limited banner
bendersej Apr 24, 2026
5e98a1d
[P059] fix: share-value not picked up mid-session + WelcomeBanner cen…
bendersej Apr 24, 2026
726d455
[P059] fix: prevent header layout shift when switching subtitle -> sw…
bendersej Apr 24, 2026
11abe6c
[P059] feat: BYOK-activated panel + narrow demo_key_rejected + extrac…
bendersej Apr 24, 2026
b50a91e
[P059] test: unit coverage for stream_error + shared_keys; narrow dem…
bendersej Apr 24, 2026
5b3478a
[P059] test: trim stream_error + shared_keys suites to behaviour-pinn…
bendersej Apr 24, 2026
a528b38
[P059] fix: code-review follow-ups (resume panel generalised + classi…
bendersej Apr 24, 2026
8252122
[P059] feat: Download CTA + ModalFooter primitive + post-stream Field…
bendersej Apr 24, 2026
4b76ba6
[P059] fix: SYSTEM prompt no longer hardcodes English
bendersej Apr 24, 2026
2cea1db
[P059] refactor: extract useDetectUserAddedField hook + gate poll on …
bendersej Apr 24, 2026
7edeefc
[P059] feat: register-interest + talk-to-us route through /contact?me…
bendersej Apr 24, 2026
517fe67
[P059] fix: tool-group icon spinner wins over error; red applies via …
bendersej Apr 24, 2026
ac76b3d
[P059] refactor: rename examples/pdf-form-copilot → form-copilot at t…
bendersej Apr 24, 2026
4574ec0
[P059] docs: .env.example audit - document the `model` field + refres…
bendersej Apr 24, 2026
d3d8ff2
[P059] feat: rate-limit panel rewrite + SocialShare with Copy link + …
bendersej Apr 24, 2026
e74342e
[P059] fix: pin engines.node 24.x + resync package-lock.json (fixes D…
bendersej Apr 24, 2026
240ff65
chore: commit package lock
bendersej Apr 24, 2026
96d48b7
[P059] chore(form-copilot): regenerate lockfile to fix DO build
bendersej Apr 24, 2026
be538f9
[P059] chore(form-copilot): add start script for DO App Platform
bendersej Apr 24, 2026
4af94d7
[P059] fix(form-copilot): blanket 500 for misconfigured env + log pre…
bendersej Apr 24, 2026
8035605
[P059] fix(form-copilot): strip shell-wrapping quotes from SHARED_API…
bendersej Apr 24, 2026
a7686f1
[P059] chore(form-copilot): drop "PDF" from product name in title + R…
bendersej Apr 25, 2026
6e57159
[P059] docs(form-copilot): overhaul README + add MIT LICENSE
bendersej Apr 25, 2026
34e9def
[P059] docs(form-copilot): align README header with the react package
bendersej Apr 25, 2026
68a0314
[P059] docs(form-copilot): strip em-dashes + add "run demo locally as…
bendersej Apr 25, 2026
1c2c7a2
[P059] docs(form-copilot): document the shared-key flow + portable ba…
bendersej Apr 25, 2026
6ae3b6d
[P059] docs(form-copilot): add the in-app production-architecture dia…
bendersej Apr 25, 2026
32ba436
[P059] chore(form-copilot): genericize S3 example values in .env.example
bendersej Apr 25, 2026
b0cc895
[P059] feat: one-click "Deploy to DigitalOcean" button
bendersej Apr 25, 2026
61e9487
[P059] fix: deploy_on_push: false in DO template
bendersej Apr 25, 2026
d33b63e
[P059] fix: remove hardcoded region from DO deploy template
bendersej Apr 25, 2026
8fc572c
[P059] copy(form-copilot): tighten healthcare info-modal body across …
bendersej Apr 25, 2026
14146e3
[P059] feat(form-copilot): Valkey/Redis rate limiter + Zod env valida…
bendersej Apr 25, 2026
48be3f1
[P059] fix(form-copilot): code-review remediation for Phase 6.1 + 6.2
bendersej Apr 25, 2026
2622a7a
[P059] feat(form-copilot): add fork-and-go skill at tool-agnostic path
bendersej Apr 25, 2026
ab5ece3
[P059] docs(form-copilot): correct plan gate from Premium to Pro
bendersej Apr 25, 2026
32f637a
[P059] docs(form-copilot): explain the localhost:3001 whitelist + por…
bendersej Apr 25, 2026
7177d7d
[P059] docs(form-copilot): Cloudflare Containers + sign-up nuance + D…
bendersej Apr 25, 2026
81bfdc6
[P059] docs(form-copilot): point at sidebar for companyIdentifier dis…
bendersej Apr 25, 2026
6a8fa1b
[P059] docs(form-copilot): exact path to whitelist origins in SimpleP…
bendersej Apr 25, 2026
a44e845
[P059] docs(form-copilot): use editor origin auto-detection in whitel…
bendersej Apr 25, 2026
1040067
[P059] feat(form-copilot): split architecture diagram into demo vs pr…
bendersej Apr 25, 2026
52f8850
[P059] copy(form-copilot): tighten architecture tab labels in info modal
bendersej Apr 25, 2026
c2c02a1
[P059] copy(form-copilot): keep Hosted AI header, put Demo LLM inside…
bendersej Apr 25, 2026
9ae3df9
[P059] copy(form-copilot): tighten architecture diagrams + drop rate-…
bendersej Apr 25, 2026
ccf18a5
[P059] style(form-copilot): strip architecture tab styling, underline…
bendersej Apr 25, 2026
b12dc30
[P059] copy(form-copilot): move architecture subtext under the tabs, …
bendersej Apr 25, 2026
4973279
[P059] copy(form-copilot): tighten architecture demo subtext (selecte…
bendersej Apr 25, 2026
83ea860
[P059] feat(form-copilot): blue header + translator pass on architect…
bendersej Apr 25, 2026
a88d8ba
[P059] style(form-copilot): make header tagline pop (white/75 + regul…
bendersej Apr 25, 2026
24a54c2
[P059] style(form-copilot): nudge header tagline down 2px so its base…
bendersej Apr 25, 2026
22e5353
[P059] feat(form-copilot): swap header logo to the white SimplePDF mark
bendersej Apr 25, 2026
4dece72
[P059] style(form-copilot): logo 28px → 34px (cumulative ~+42% from t…
bendersej Apr 25, 2026
8df97ca
[P059] style(form-copilot): light-green chat pane background (bg-emer…
bendersej Apr 25, 2026
b8ac6de
[P059] style: use emerald border on chat aside (revert bg)
bendersej Apr 25, 2026
abec4c0
[P059] style: drop pre-signed URLs from arch + sky-700 chat footer
bendersej Apr 25, 2026
7084e8a
[P059] style: revert sky-700 chat footer
bendersej Apr 25, 2026
9f26fd4
[P059] style: emerald-500 focus border on chat input
bendersej Apr 25, 2026
578f443
[P059] style: revert emerald focus border on chat input
bendersej Apr 25, 2026
a7905b7
[P059] copy: change chat input placeholder to "Talk to Form Copilot…"
bendersej Apr 25, 2026
087e7fb
[P059] style: round chat input + circular Send button with arrow-up icon
bendersej Apr 25, 2026
d491bf0
[P059] style: use lucide ArrowUp for Send icon
bendersej Apr 25, 2026
3eaca02
[P059] style: swap Send icon to lucide ArrowBigUp
bendersej Apr 25, 2026
ff20f3f
[P059] style: revert to ArrowUp for Send icon
bendersej Apr 25, 2026
6ac1757
[P059] style: thicken Send icon stroke to 3
bendersej Apr 25, 2026
0dfb143
[P059] fix(form-copilot): pin @emnapi/core+runtime as optional deps
bendersej Apr 25, 2026
1a3e3c5
[P059] refactor(form-copilot): hoist chat status to one memo, drop du…
bendersej Apr 25, 2026
305cb20
[P059] feat(form-copilot): mode-aware finalisation (submit for Pro, d…
bendersej Apr 25, 2026
8c0204c
[P059] fix(form-copilot): auto-close Dropdown when disabled flips to …
bendersej Apr 25, 2026
6eedb04
[P059] fix(form-copilot): revert dropdown auto-close, drop disabled={…
bendersej Apr 25, 2026
9fcd95b
[P059] fix(form-copilot): allow collapsing ToolInvocationGroup disclo…
bendersej Apr 25, 2026
8cfcf6e
[P059] refactor(form-copilot): typed finalisation helper, rename Mode…
bendersej Apr 25, 2026
ee69d1e
[P059] feat(form-copilot): first-load welcome modal + meta image
bendersej Apr 25, 2026
e00dae6
[P059] feat(form-copilot): SSR welcome modal via cookie + Accept-Lang…
bendersej Apr 25, 2026
86ebbf3
[P059] feat(form-copilot): per-locale Trans-based tagline highlight +…
bendersej Apr 25, 2026
6d4202a
[P059] fix(form-copilot): bound Redis connect timeout + retry strategy
bendersej Apr 25, 2026
52e5513
[P059] docs(form-copilot): document DO App Platform "attach from the …
bendersej Apr 25, 2026
6d84a8f
[P059] fix(form-copilot): stop leaking upstream HTML errors to the ch…
bendersej Apr 25, 2026
bdf6bce
[P059] fix(form-copilot): never give up on Redis reconnect
bendersej Apr 25, 2026
7048476
[P059] feat(form-copilot): offer BYOK escape hatch on service-unavail…
bendersej Apr 25, 2026
12cfce7
[P059] docs(form-copilot): note DATABASE_URL → REDIS_URL alias step
bendersej Apr 25, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 28 additions & 0 deletions .do/deploy.template.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
spec:
name: form-copilot
services:
- name: web
github:
repo: SimplePDF/simplepdf-embed
branch: main
deploy_on_push: false
source_dir: /form-copilot
environment_slug: node-js
build_command: npm run build
run_command: npm start
http_port: 3000
instance_count: 1
instance_size_slug: apps-s-1vcpu-1gb
envs:
- key: VITE_SIMPLEPDF_COMPANY_IDENTIFIER
scope: BUILD_TIME
type: GENERAL
- key: SHARED_API_KEYS
scope: RUN_TIME
type: SECRET
- key: REDIS_URL
scope: RUN_TIME
type: SECRET
- key: IP_HASH_SALT
scope: RUN_TIME
type: SECRET
19 changes: 19 additions & 0 deletions form-copilot/.cta.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"projectName": "form-copilot",
"mode": "file-router",
"typescript": true,
"packageManager": "npm",
"includeExamples": false,
"tailwind": true,
"addOnOptions": {},
"envVarValues": {},
"git": false,
"install": true,
"routerOnly": false,
"version": 1,
"framework": "react",
"chosenAddOns": [
"biome",
"nitro"
]
}
102 changes: 102 additions & 0 deletions form-copilot/.env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
# =============================================================================
# Access keys (OPTIONAL: drives the invite-link `?share=<id>` flow)
# =============================================================================
#
# Leave SHARED_API_KEYS unset and the demo runs in BYOK-only mode: every
# visitor brings their own provider key via the in-app Model Picker, and
# the stream goes browser-direct (never through this server). This is
# the easiest setup if you only need it for yourself.
#
# Set SHARED_API_KEYS to skip BYOK and let non-technical viewers chat
# without pasting a key. Each entry maps a share id to an api key, a
# model handle, and a per-IP lifetime cap on chat turns. Visitors land
# on `?share=<id>` and the server pays for the LLM under your account.
# Requests without a recognized `?share=<id>` still return 401 and the
# client falls back to BYOK.
#
# Shape (per share id):
#
# {
# "<share_id>": {
# "api_key": "<provider api key>",
# "model": "<anthropic_haiku_4_5 | deepseek_v4_flash>",
# "rate_limit_turns_lifetime": 20
# }
# }
#
# Each share id has its own per-IP lifetime counter. With REDIS_URL set
# (see below), counters survive restarts and are shared across containers.
# Without REDIS_URL, counters live in memory per container (fine for
# single-instance / BYOK-only deployments). The reserved id "__default__"
# is rejected at parse time. Example with two invites on two providers:
#
# SHARED_API_KEYS='{"preview-alice":{"api_key":"sk-ant-...","model":"anthropic_haiku_4_5","rate_limit_turns_lifetime":5},"customer-acme":{"api_key":"sk-...","model":"deepseek_v4_flash","rate_limit_turns_lifetime":100}}'
#
# Visitors then open `http://localhost:3001/?share=preview-alice` (or
# the equivalent on your hosted domain).
#
# -----------------------------------------------------------------------------
# Base64 fallback (recommended for hosted deploys)
# -----------------------------------------------------------------------------
# Some hosts (DigitalOcean App Platform, Render, fly.io app specs) mangle
# embedded or surrounding quotes when storing JSON-shaped env vars. The
# parser accepts a base64-encoded payload as an alternative: plain JSON
# is tried first, base64-then-JSON second. Either works.
#
# Portable one-liner (macOS + Linux):
#
# printf '%s' '{"preview-alice":{"api_key":"sk-ant-...","model":"anthropic_haiku_4_5","rate_limit_turns_lifetime":5}}' | base64 | tr -d '\n'
#
# Paste the resulting ASCII-only string as the value, with no surrounding
# quotes. The parser also auto-strips one wrapping pair of single or
# double quotes if your host shell-escapes the value into the env.
SHARED_API_KEYS=

# =============================================================================
# Client configuration
# =============================================================================

# SimplePDF company identifier. The subdomain piece of
# <companyIdentifier>.simplepdf.com that serves the embedded editor.
# Exposed to the browser (VITE_ prefix) because the iframe src is built
# client-side. REQUIRED: the app throws at startup if unset.
#
# `form-copilot` is the public demo workspace and whitelists
# `http://localhost:3001`, so the iframe loads as-is for local dev. To
# host this on your own domain, set this to your own SimplePDF
# Premium company identifier and whitelist your serving origin in the
# SimplePDF dashboard.
VITE_SIMPLEPDF_COMPANY_IDENTIFIER=form-copilot


# Optional. Gates the TanStack Router devtools panel + chatty monitoring
# logs. Leave unset in production / CI; set to "true" for local dev.
# VITE_ENABLE_DEVTOOLS=true

# =============================================================================
# Rate-limit storage (optional; required for multi-container deployments)
# =============================================================================

# Connection URL to a Redis-protocol-compatible instance (Valkey, Redis,
# KeyDB, etc.). Required for hosted deployments where you want
# per-(share, IP) lifetime counters to survive restarts AND be shared
# across multiple containers. Without it, counters live in memory per
# container (fine for local dev, single-instance hosts, or BYOK-only
# deployments).
#
# DO Managed Caching for Valkey: $15/mo single-node, drop-in protocol-
# compatible. The dashboard returns a `rediss://default:<password>@<host>:25061/0`
# URL. The password is INSIDE the URL, so this value is a secret and must
# be stored as such (the deploy.template.yaml at the repo root marks it
# `type: SECRET`).
# REDIS_URL=rediss://default:<password>@<host>:25061/0

# REQUIRED when REDIS_URL is set (the server refuses to boot otherwise).
# Salts the SHA-256 IP hash used in Redis keys (rl:<share>:<hash>).
# Without a salt, a leak of the Redis snapshot lets anyone brute-force the
# ~4B IPv4 space in minutes. With a salt, the brute force needs the
# server-side secret too. Optional in BYOK-only mode (no REDIS_URL set),
# since hashes never persist beyond the process.
#
# Generate with: openssl rand -hex 32
# IP_HASH_SALT=
13 changes: 13 additions & 0 deletions form-copilot/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
node_modules
.DS_Store
dist
dist-ssr
*.local
.env
.nitro
.tanstack
.wrangler
.output
.vinxi
__unconfig*
todos.json
35 changes: 35 additions & 0 deletions form-copilot/.vscode/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
{
"files.watcherExclude": {
"**/routeTree.gen.ts": true
},
"search.exclude": {
"**/routeTree.gen.ts": true
},
"files.readonlyInclude": {
"**/routeTree.gen.ts": true
},
"[javascript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[javascriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescript]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[typescriptreact]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[json]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[jsonc]": {
"editor.defaultFormatter": "biomejs.biome"
},
"[css]": {
"editor.defaultFormatter": "biomejs.biome"
},
"editor.codeActionsOnSave": {
"source.organizeImports.biome": "explicit"
}
}
22 changes: 22 additions & 0 deletions form-copilot/LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
The MIT License (MIT)

Copyright (c) SimplePDF Engineering <engineering@simplepdf.com>
(https://simplepdf.com)

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.
Loading