@wenathlan/cobaia 0.0.0-stage → 2.0.158
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/App.tsx +52 -0
- package/Dockerfile +48 -0
- package/_headers +4 -0
- package/_redirects +1 -0
- package/adsconfig.ts +59 -0
- package/biome.jsonc +134 -0
- package/bun.lock +2451 -0
- package/capacitor.config.ts +51 -0
- package/checkoutlinks.ts +80 -0
- package/cli.ts +114 -0
- package/cobaia.csproj +22 -0
- package/cobaia.gemspec +14 -0
- package/cobaia.java +16 -0
- package/content.ts +425 -0
- package/db.ts +219 -0
- package/devthink.toml +5 -0
- package/docs/features.md +58 -0
- package/docs/monetization.md +61 -0
- package/familyurl.ts +22 -0
- package/favicon.svg +1 -0
- package/icon.svg +1 -0
- package/index.html +26 -0
- package/labels.ts +291 -0
- package/manifest.webmanifest +56 -0
- package/mime.types +12 -0
- package/netlify.toml +2 -0
- package/package.json +207 -4
- package/pnpm-workspace.yaml +101 -0
- package/pnpmboot.package-lock.json +157 -0
- package/pnpmboot.package.json +6 -0
- package/pom.xml +29 -0
- package/posts.ts +54 -0
- package/robots.txt +90 -0
- package/roster.ts +29 -0
- package/sandboxmodality.ts +138 -0
- package/schema.prisma +31 -0
- package/sitemap.xml +42 -0
- package/sol/Sol.tsx +91 -0
- package/sol/apps/apps.tsx +66 -0
- package/sol/blog/blog.tsx +86 -0
- package/sol/home/home.tsx +140 -0
- package/sol/intro/intro.tsx +187 -0
- package/sol/legal/legal.tsx +76 -0
- package/sol/notfound/notfound.tsx +32 -0
- package/sol/pricing/pricing.tsx +98 -0
- package/sol/shell/Shell.tsx +161 -0
- package/sol/sol.css +1951 -0
- package/sol/support/support.tsx +102 -0
- package/sol/wingets/IntroFooter.tsx +69 -0
- package/sol/wingets/IntroPlans.tsx +93 -0
- package/sol/wingets/IntroSplash.tsx +105 -0
- package/sol/wingets/ShellAdsslot.tsx +47 -0
- package/sol/wingets/ShellBrand.tsx +86 -0
- package/sol/wingets/index.ts +18 -0
- package/tests/adsconfig.test.ts +51 -0
- package/tests/checkoutlinks.test.ts +73 -0
- package/tests/familyurl.test.ts +40 -0
- package/tests/posts.test.ts +76 -0
- package/tests/sandboxmodality.test.ts +77 -0
- package/tsconfig.json +22 -0
- package/tsconfig.node.json +22 -0
- package/vercel.json +3 -0
- package/vite.config.ts +44 -0
- package/vitest.config.ts +42 -0
- package/wrangler.toml +7 -0
- package/README.md +0 -3
package/docs/features.md
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# cobaia features
|
|
2
|
+
|
|
3
|
+
the real feature list of the cobaia content site, extracted from the code: every item below exists in the app root logics or the Sol theme.
|
|
4
|
+
|
|
5
|
+
## plans
|
|
6
|
+
|
|
7
|
+
- three mrr plans: free at us$0, pro at us$5 and studio at us$19 per month, placeholder prices until the billing platform decision lands (stripe payment links or paddle)
|
|
8
|
+
- honest checkout state: a configured plan href renders a live anchor, an empty one renders the disabled button with the note checkout not configured yet
|
|
9
|
+
- plan lookup: planbyid answers one plan of the table by its stable id (free, pro, studio)
|
|
10
|
+
- plan prose in the label store: every plan sentence (name, billing period, blurb, features) answers from labels.ts; only the billing values ride checkoutlinks.ts
|
|
11
|
+
- intro plans zone: the intro page renders the same offer from the one checkoutlinks table, the studio plan leading raised
|
|
12
|
+
- what the money covers: the pricing section states the us$15 a month floor of domains and free static hosting and that the applications stay ad free
|
|
13
|
+
- billing webhook walk lane: the plan-checkout workload lane is declared in sandboxmodality.ts for the billing webhook work when it lands, secrets server side
|
|
14
|
+
|
|
15
|
+
## ads
|
|
16
|
+
|
|
17
|
+
- reserved slot registry: one slot per content page, home display at 728/90, apps in-feed at 3/1, blog in-article at 3/1, keyed by page folder name
|
|
18
|
+
- honest ad container: a fixed aspect-ratio box on a hairline well, visibly labeled advertisement, rendering the empty note ad slot not configured yet while no slot id is set, so the page never shifts when a real unit arrives
|
|
19
|
+
- script rule: no ad network script lives in the repository; the adsense or equivalent loader is injected after network approval through the deploy environment only
|
|
20
|
+
- slot liveness check: adsslotlive answers whether a reserved slot carries a real network id
|
|
21
|
+
- ad free surfaces: the pricing, support and legal pages carry no ad slot; nothing that asks for money or discloses policy sits beside an advertisement
|
|
22
|
+
- ads slot lane: the ads-slot workload lane is declared for the reserved slots and the rotation decision the deploy answers before the render
|
|
23
|
+
|
|
24
|
+
## content
|
|
25
|
+
|
|
26
|
+
- content lane: the pages read the live rows the platform gateway serves over GET /api/v1/ui/content and fall back to the manifest seed in content.ts, so the interface never blanks
|
|
27
|
+
- content kinds: the lane serves posts, roster, plans, supportlinks, ads.slots, ads.label and ads.empty, each row an ordinal and a json payload
|
|
28
|
+
- live load discipline: one fetch per session against the configured base (VITE_CATALOG_URL) with a 4 second abort window; any failure keeps the seed answering
|
|
29
|
+
- useContent mount hook: a store-riding page loads the live rows once per mount and re-renders when they land, with no first-paint flash
|
|
30
|
+
- update blog: three seed posts with full bodies (ten sites one engine, one css one tree, no backend no tracking), the index sorted newest first and each post carrying date, tags, excerpt, body and the honest word count
|
|
31
|
+
- app directory: the ten deploy units of the family, each card carrying the one-line role declared in its own devthink.toml and its spec accent
|
|
32
|
+
- label store: every interface string rides the label manifest at revision 4, read through the typed accessors label and labelfill, seeded into the store with translated rows protected from overwrite
|
|
33
|
+
- label seed lane: the label-seed workload lane is declared for the manifest walk the sqlite vault carries
|
|
34
|
+
|
|
35
|
+
## support
|
|
36
|
+
|
|
37
|
+
- two support channels: github sponsors and ko-fi, both presented as zero platform fee, read from the supportlinks record
|
|
38
|
+
- honest channel state: an unconfigured channel renders the disabled button with the note link not configured yet, never a fake link
|
|
39
|
+
- other ways to help: the section lists the public issue tracker, writing about the tools and linking the directory
|
|
40
|
+
- invalid traffic policy: the site states no ad clicking, no traffic exchange, no bots, because automated traffic against an ad surface kills the ad account and the domain reputation
|
|
41
|
+
- support strip on home: the landing carries the support call before the reserved ad slot
|
|
42
|
+
|
|
43
|
+
## ui
|
|
44
|
+
|
|
45
|
+
- one shared shell: the sticky acrylic nav with the active section indicator and the worked footer carrying the family row, the same surface at the direct url and inside the os power clip
|
|
46
|
+
- routes: home on the bare root, apps, pricing, support, blog, legal inside the shell, the bare intro at /intro and the catch-all 404
|
|
47
|
+
- intro splash: the akash opening with the mark spring, name rise and role fade, skippable by click, tap or key, honoring reduced motion and writing nothing to the visitor machine
|
|
48
|
+
- brand mark: the drawn beaker svg in the dark amber gradient, the only seat of the identity accent, with a gentle hover tilt
|
|
49
|
+
- home landing: left-aligned hero, the three steps (engine, static, honest), the four-card featured directory preview and the support strip
|
|
50
|
+
- legal page: the privacy policy and terms with clause leads, disclosing the advertising plan and the analytics rule
|
|
51
|
+
- notfound page: the 404 answer inside the site chrome with the way out to home and the directory
|
|
52
|
+
- window captions: the shell carries the minimize, maximize and close caption labels for the os window seat
|
|
53
|
+
- wouter routing on the declared vite base: deep links resolve against the pages subpath the build declaration owns
|
|
54
|
+
- static deploys: netlify, vercel and cloudflare workers configs at the app root, plus robots.txt and sitemap.xml for the crawlers
|
|
55
|
+
|
|
56
|
+
## Dependencies
|
|
57
|
+
|
|
58
|
+
- synchronized dependency union — the eleven family apps share one dependency tree: identical `dependencies`, `devDependencies` and `peerDependencies` (same keys, same versions, nothing removed, highest semver wins on conflicts) and one `engines` catalog with node as maestro and the V 0.5.2 seat as catalog annotation (@family wave/depssync).
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
# cobaia monetization surface
|
|
2
|
+
|
|
3
|
+
cobaia is the public monetization surface of the devthink family: the content site that carries the mrr plan table, the support channels and the honest advertisement slots while the ten family applications stay clean.
|
|
4
|
+
|
|
5
|
+
## overview
|
|
6
|
+
|
|
7
|
+
- a plain content site with no entry gate and no onboarding flow: every page is public and the home renders directly on the bare root
|
|
8
|
+
- one window shell (header, footer) wraps every content route; the intro page mounts bare at /intro outside the chrome
|
|
9
|
+
- every plan href and support href ships empty until the owner configures billing; the interface renders honest empty states instead of dead links
|
|
10
|
+
- no ad network script is committed: the loader enters only after network approval, injected through the deploy environment
|
|
11
|
+
- zero tracking: no cookies, no local storage, no analytics beacons, no keys committed
|
|
12
|
+
- build tooling: vite static build, wouter router on the declared vite base, vitest suites, biome formatting
|
|
13
|
+
|
|
14
|
+
## module inventory
|
|
15
|
+
|
|
16
|
+
the pure logics at the app root, each one a typed accessor over the content lane:
|
|
17
|
+
|
|
18
|
+
- adsconfig.ts: the honest ad slot registry. exports the adsslot type (slotid, format, ratio), the fixed label advertisement, the empty note ad slot not configured yet, the reserved slots per page (home display 728/90, apps in-feed 3/1, blog in-article 3/1) and the readers adsslotfor and adsslotlive
|
|
19
|
+
- checkoutlinks.ts: the typed billing accessor. checkoutlinks answers the plan table, supportlinks the support channel record, isconfigured tests an href, planbyid finds one plan by id (free, pro, studio). only billing values ride here; the plan prose answers from the label store
|
|
20
|
+
- content.ts: the content law. section 1 holds the manifest seed (seedPosts, seedRoster, seedPlans, seedSupportLinks, seedAdsSlots, seedAdsLabel, seedAdsEmpty); section 2 holds the live loader (loadcontent over GET /api/v1/ui/content), the accessors contenttable and contentvalue (live rows first, seed fallback) and the useContent mount hook
|
|
21
|
+
- posts.ts: the blog accessor. posts answers the post table, postindex sorts it newest first, postwords counts the honest word total of one body
|
|
22
|
+
- roster.ts: the family directory accessor. roster answers the ten deploy units with slug, accent and one-line role, in rail order with the os first
|
|
23
|
+
- db.ts: the self-hosted sqlite runtime (better-sqlite3 through the vault storagelane). keeps the onboarding_states and labels tables with prepared, parameterized statements only
|
|
24
|
+
- familyurl.ts: the family address book. familyurl resolves one sibling deploy unit to the relative url ../slug/ with the trailing slash, so the family links both ways
|
|
25
|
+
- labels.ts: the copy law. the english manifest (LABEL_MANIFEST, revision 4) carries every interface string keyed by page.widget.key; label and labelfill read it; seedLabels mirrors the rows into the browser label store
|
|
26
|
+
- sandboxmodality.ts: the workload lane declaration. mirrors the three lanes cobaia owns in the family catalog (plan-checkout, ads-slot, label-seed) and reads the COBAIA_SANDBOX_LANE seat (local, join, or a pinned modality id)
|
|
27
|
+
- capacitor.config.ts, vite.config.ts, vitest.config.ts: the mobile conversion config, the static build and the test runner configs at the app root
|
|
28
|
+
|
|
29
|
+
## data layer
|
|
30
|
+
|
|
31
|
+
- the sqlite runtime opens one storage lane on the app-owned file cobaia.db (wal, foreign keys, patient locks) through openStorageLane of @wenathlan/vault
|
|
32
|
+
- every statement is prepared and bound; no sql is assembled by string concatenation
|
|
33
|
+
- the boot ddl is idempotent and mirrors schema.prisma, the documentation of record: the tables exist after the first open
|
|
34
|
+
- onboarding_states holds the entry-flow flags per visitor (introseen, onboardingseen) with a seen-wins upsert; the platform gateway serves the same flags over /api/v1/onboarding
|
|
35
|
+
- labels holds the interface copy store: id, text, revision, translated. the boot seed ingests the english manifest and never overwrites a translated row
|
|
36
|
+
- the content rows (posts, roster, plans, supportlinks, ads.slots, ads.label, ads.empty) live in the site store and answer over the platform gateway at GET /api/v1/ui/content; the manifest seed in content.ts is the fallback the offline static build reads
|
|
37
|
+
- the browser never imports db.ts: better-sqlite3 cannot ride the static bundle, so the browser pulls the live rows over the internal api (base from VITE_CATALOG_URL, 4 second abort window, one load per session)
|
|
38
|
+
- the vitest suites (adsconfig, checkoutlinks, familyurl, posts, sandboxmodality) pin the module contracts: the slot registry, the placeholder prices, the empty hrefs and the plan prose keys
|
|
39
|
+
|
|
40
|
+
## sol subapps
|
|
41
|
+
|
|
42
|
+
the theme surface, one folder per page, mounted by the theme anchor sol/Sol.tsx on wouter:
|
|
43
|
+
|
|
44
|
+
- home: the landing. left-aligned hero, the three steps (engine, static, honest), a four-card directory preview reading the one roster table (devthink, saddle, vault, cadria), the support strip and the reserved ad slot at the end of the content
|
|
45
|
+
- apps: the directory. one card per deploy unit with the honest role and the deep link through familyurl, closed by the in-feed ad slot
|
|
46
|
+
- pricing: the plan table. three honest plans (free, pro, studio), a configured href renders a live anchor, an empty one the disabled button plus the note checkout not configured yet, and the money section explains the us$15 a month floor. no ad slot on this page
|
|
47
|
+
- support: the support channels. github sponsors and ko-fi cards with the same configured-or-honest rule, the other ways to help section and the invalid traffic line. no ad slot on this page
|
|
48
|
+
- blog: the update blog. the sorted index and the full bodies of the posts as real articles with date, word count and tags in the meta line, closed by the in-article ad slot
|
|
49
|
+
- legal: the privacy policy and the terms. discloses the advertising plan (labeled reserved slots, third-party cookies only once a network is live) and the analytics rule (none by default). no ad slot on this page
|
|
50
|
+
- intro: the bare presentation page. the akash splash (plays per visit, writes nothing, skips on click, tap or key), the plans zone reading the one checkoutlinks table, the door panes and the footer doors
|
|
51
|
+
- notfound: the 404 answer inside the site chrome
|
|
52
|
+
- shell: the one chrome. the sticky acrylic nav with the active section indicator, the footer carrying the family row through familyurl, and the embedded contract that renders the same surface inside the devthink os power clip
|
|
53
|
+
- wingets: the shared components (IntroSplash, IntroPlans, IntroFooter, ShellAdsslot, ShellBrand) re-exported through one index surface
|
|
54
|
+
|
|
55
|
+
## family integration
|
|
56
|
+
|
|
57
|
+
- the roster mirrors the ten deploy units: devthink the os, saddle the sandbox, forge the execution clone, foundry the complete clone, vault the storage clone, getry the ai workspace, cadria the video and image studio, debonair the audio daw, stealthhead the fps game platform, argan the dns toolkit, each with its spec section 12 accent
|
|
58
|
+
- familyurl resolves every sibling one level above this deploy unit, so the directory links out and every sibling footer links back
|
|
59
|
+
- the label store rides the shared vault labelstore; the sqlite lane rides the shared vault storagelane, the same mechanism the storage clone keeps
|
|
60
|
+
- the deploy targets are static: netlify, vercel and cloudflare workers configs sit at the app root beside robots.txt and sitemap.xml; the capacitor mobile lane renders the same web bundle
|
|
61
|
+
- the package identity is @wenathlan/cobaia inside the devthink monorepo; the devthink.toml declares the app role and the seven pages (home, pricing, support, apps, legal, blog, notfound)
|
package/familyurl.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* familyurl.ts — the address book of the family deploy units that share the
|
|
3
|
+
* host with the cobaia. The family redirects both ways: the site directory
|
|
4
|
+
* (/apps) links every sibling application and the DevThink OS, and every
|
|
5
|
+
* sibling links back through its own copy of this helper. The deploy units
|
|
6
|
+
* sit beside this one as sibling folders on the shared origin, so the url
|
|
7
|
+
* resolves one level above this application (../slug/), with the slug
|
|
8
|
+
* trimmed of any slashes so both bare names and pasted segments resolve to
|
|
9
|
+
* the same address. Pure and side-effect free: the helper only shapes
|
|
10
|
+
* strings.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Resolves the url of a family deploy unit that shares this origin.
|
|
15
|
+
*
|
|
16
|
+
* @param slug the deploy unit segment (the app folder name).
|
|
17
|
+
* @returns the relative url of the family application, one level above this
|
|
18
|
+
* deploy unit, always carrying the trailing slash.
|
|
19
|
+
*/
|
|
20
|
+
export function familyurl(slug: string): string {
|
|
21
|
+
return `../${slug.replace(/^\/+|\/+$/g, "")}/`;
|
|
22
|
+
}
|
package/favicon.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32" role="img" aria-label="cobaia favicon"><title>cobaia</title><rect width="32" height="32" rx="8" fill="#b45309"/></svg>
|
package/icon.svg
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 96 96" role="img" aria-label="cobaia icon"><title>cobaia</title><defs><linearGradient id="cbbg" x1="0" y1="0" x2="0" y2="1"><stop offset="0" stop-color="#f59e0b"/><stop offset="1" stop-color="#b45309"/></linearGradient></defs><rect width="96" height="96" rx="24" fill="url(#cbbg)"/><g fill="none" stroke="#fbf5ea" stroke-width="5" stroke-linecap="round" stroke-linejoin="round"><path d="M38 26 H58 M42 26 V44 L30 66 A4 4 0 0 0 34 72 H62 A4 4 0 0 0 66 66 L54 44 V26"/><path d="M36 56 H60"/></g></svg>
|
package/index.html
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<!-- web entry of the theme -->
|
|
3
|
+
<html lang="en">
|
|
4
|
+
<head>
|
|
5
|
+
<meta charset="utf-8" />
|
|
6
|
+
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport" />
|
|
7
|
+
<meta
|
|
8
|
+
name="description"
|
|
9
|
+
content="cobaia is the front door of the DevThink family: the pricing, the support page, the app directory and the update blog for ten deployable web applications."
|
|
10
|
+
/>
|
|
11
|
+
<meta name="theme-color" content="#0a0a0b" />
|
|
12
|
+
<link rel="icon" href="./favicon.svg" />
|
|
13
|
+
<link rel="manifest" href="./manifest.webmanifest" />
|
|
14
|
+
<link rel="preconnect" href="https://fonts.googleapis.com" />
|
|
15
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
|
|
16
|
+
<link
|
|
17
|
+
href="https://fonts.googleapis.com/css2?family=Inter:wght@100..900&family=Geist+Mono:wght@100..900&display=swap"
|
|
18
|
+
rel="stylesheet"
|
|
19
|
+
/>
|
|
20
|
+
<title>cobaia — the devthink family site</title>
|
|
21
|
+
<script type="module" src="./App.tsx"></script>
|
|
22
|
+
</head>
|
|
23
|
+
<body>
|
|
24
|
+
<div id="root"></div>
|
|
25
|
+
</body>
|
|
26
|
+
</html>
|
package/labels.ts
ADDED
|
@@ -0,0 +1,291 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* labels.ts — the copy law of cobaia: every interface string lives in the
|
|
3
|
+
* label store, the wingets and page anchors read through this typed
|
|
4
|
+
* accessor, and a hardcoded string inside a widget is a defect. the
|
|
5
|
+
* manifest below is the english source of truth extracted from the real
|
|
6
|
+
* strings of the Sol wingets and the page anchors; the seed ingests it
|
|
7
|
+
* into the labels store (the site db through db.ts on the server, the
|
|
8
|
+
* browser rows cache in the visitor machine) and the seed never overwrites
|
|
9
|
+
* a translated row.
|
|
10
|
+
*
|
|
11
|
+
* the browser never imports db.ts (better-sqlite3 cannot ride the static
|
|
12
|
+
* bundle): the type comes across erased, the browser seed mirrors the rows
|
|
13
|
+
* into the local cache.
|
|
14
|
+
*
|
|
15
|
+
* data stays data: the app roster (apps.roster.ts — slugs, accents, the
|
|
16
|
+
* roles each app declares in its own devthink.toml), the plan billing
|
|
17
|
+
* values and the support hrefs (checkoutlinks.ts — ids, prices, checkout
|
|
18
|
+
* links; the plan prose itself rides here), the ad slot registry
|
|
19
|
+
* (adsconfig.ts — the fixed ad contract label and note ride there, pinned
|
|
20
|
+
* by the tests), the blog posts (posts.ts) and the route hrefs keep
|
|
21
|
+
* flowing from the data modules; the prose and the affordances around
|
|
22
|
+
* them ride here.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { createLabelStore } from "@wenathlan/vault/labelstore";
|
|
26
|
+
|
|
27
|
+
/** the label revision of the manifest: bump on every copy wave so the seed
|
|
28
|
+
* refreshes the store rows of the changed strings. */
|
|
29
|
+
export const LABEL_REVISION = 4;
|
|
30
|
+
|
|
31
|
+
/** the english manifest: the page.widget.key path answers the surface text.
|
|
32
|
+
* every entry is the verbatim string the wingets and page anchors rendered
|
|
33
|
+
* before the copy wave — zero invented copy. */
|
|
34
|
+
export const LABEL_MANIFEST = {
|
|
35
|
+
// the window shell widget (the one chrome of the content site)
|
|
36
|
+
"shell.skip": "skip to content",
|
|
37
|
+
"shell.window.aria": "cobaia",
|
|
38
|
+
"shell.brand.aria": "cobaia — home",
|
|
39
|
+
"shell.name": "cobaia",
|
|
40
|
+
"shell.role": "the devthink family site",
|
|
41
|
+
"shell.caption.minimize": "Minimize cobaia",
|
|
42
|
+
"shell.caption.maximize": "Maximize cobaia",
|
|
43
|
+
"shell.caption.close": "Close cobaia",
|
|
44
|
+
"shell.nav.aria": "site sections",
|
|
45
|
+
"shell.nav.apps": "apps",
|
|
46
|
+
"shell.nav.pricing": "pricing",
|
|
47
|
+
"shell.nav.blog": "blog",
|
|
48
|
+
"shell.nav.support": "support",
|
|
49
|
+
"shell.foot.aboutHead": "cobaia",
|
|
50
|
+
"shell.foot.aboutText":
|
|
51
|
+
"the front door of the devthink family: ten applications that run anywhere, tracked nowhere. pricing, support, the app directory and the update blog live here.",
|
|
52
|
+
"shell.foot.siteHead": "site",
|
|
53
|
+
"shell.foot.apps": "apps",
|
|
54
|
+
"shell.foot.pricing": "pricing",
|
|
55
|
+
"shell.foot.blog": "blog",
|
|
56
|
+
"shell.foot.support": "support",
|
|
57
|
+
"shell.foot.legal": "legal",
|
|
58
|
+
"shell.foot.familyHead": "the family",
|
|
59
|
+
"shell.colophon": "cobaia — a provisional name. part of the devthink family · ten honest tools under one roof.",
|
|
60
|
+
// the home page anchor (the landing)
|
|
61
|
+
"home.aria.hero": "cobaia — the devthink family site",
|
|
62
|
+
"home.eyebrow": "the devthink family site",
|
|
63
|
+
"home.title": "One family. Ten tools. Zero tracking.",
|
|
64
|
+
"home.lede":
|
|
65
|
+
"devthink builds and ships a family of web applications — sandboxes, studios, tools and a game platform — hosted free, tracked nowhere. this site is the front door: the directory, the pricing, the support page and the update blog.",
|
|
66
|
+
"home.action.pricing": "see the pricing",
|
|
67
|
+
"home.action.apps": "browse the ten apps",
|
|
68
|
+
"home.section.stepsAria": "what the family is made of",
|
|
69
|
+
"home.section.stepsTitle": "What the family is made of",
|
|
70
|
+
"home.section.stepsLede": "the same three choices behind every member of the family.",
|
|
71
|
+
"home.steps.engine.n": "01",
|
|
72
|
+
"home.steps.engine.title": "one core",
|
|
73
|
+
"home.steps.engine.text":
|
|
74
|
+
"the sandbox and the storage are shared parts. every application assembles the parts it needs and ships beside the others.",
|
|
75
|
+
"home.steps.static.n": "02",
|
|
76
|
+
"home.steps.static.title": "static first",
|
|
77
|
+
"home.steps.static.text":
|
|
78
|
+
"every public surface builds down to one stylesheet and a handful of bundles on free static hosting. nothing is stored on the visitor machine — no cookies, no accounts, no tracking.",
|
|
79
|
+
"home.steps.honest.n": "03",
|
|
80
|
+
"home.steps.honest.title": "honest money",
|
|
81
|
+
"home.steps.honest.text":
|
|
82
|
+
"the family runs on about us$15 a month of domains. the pricing and the support page exist to cover that floor first — and the apps themselves stay free of ads.",
|
|
83
|
+
"home.section.previewAria": "a preview of the app directory",
|
|
84
|
+
"home.section.previewTitle": "A few of the ten",
|
|
85
|
+
"home.section.previewLede": "every member lives beside this site, one link away. the full directory lives on",
|
|
86
|
+
"home.section.previewLink": "/apps",
|
|
87
|
+
"home.section.previewTail": ".",
|
|
88
|
+
"home.appcard.open": "open {slug}",
|
|
89
|
+
"home.section.supportAria": "supporting the family",
|
|
90
|
+
"home.section.supportTitle": "Keep the family independent",
|
|
91
|
+
"home.section.supportLede":
|
|
92
|
+
"github sponsors and ko-fi keep the domains paid and the tools free — both charge zero platform fees, so the support goes straight to the hosting floor.",
|
|
93
|
+
"home.action.support": "ways to support",
|
|
94
|
+
// the apps page anchor (the directory)
|
|
95
|
+
"apps.aria.hero": "the devthink app directory",
|
|
96
|
+
"apps.eyebrow": "the directory",
|
|
97
|
+
"apps.title": "Ten apps. One family.",
|
|
98
|
+
"apps.lede":
|
|
99
|
+
"every member of the family lives beside this site and links back to it. the roles below are the honest ones each app declares for itself; the accents are the family's spec table.",
|
|
100
|
+
"apps.section.rosterAria": "the family roster",
|
|
101
|
+
"apps.card.open": "open {slug}",
|
|
102
|
+
// the pricing page anchor (the plans)
|
|
103
|
+
"pricing.action.choose": "choose {name}",
|
|
104
|
+
"pricing.action.notConfigured": "checkout not configured yet",
|
|
105
|
+
"pricing.aria.hero": "cobaia pricing",
|
|
106
|
+
"pricing.eyebrow": "pricing",
|
|
107
|
+
"pricing.title": "Pay for what you use. Or use it free.",
|
|
108
|
+
"pricing.lede":
|
|
109
|
+
"the core surface of every application is free and stays free. the paid plans add the pro features of one app or of the whole family. the prices below are placeholders until billing goes live — a plan activates the moment its checkout link is configured, and says so honestly until then.",
|
|
110
|
+
"pricing.section.plansAria": "the plans",
|
|
111
|
+
"pricing.card.planAria": "{name} plan",
|
|
112
|
+
"pricing.section.moneyAria": "what the money covers",
|
|
113
|
+
"pricing.section.moneyTitle": "What the money covers",
|
|
114
|
+
"pricing.section.moneyLede":
|
|
115
|
+
"the family runs on about us$15 a month of domains and free static hosting. the paid plans cover that floor first, then the higher tiers. there are no ads inside the applications — the ad slots live only on this site's content pages, and they are labeled and reserved.",
|
|
116
|
+
"pricing.section.moneyNote":
|
|
117
|
+
"billing decision pending: stripe payment links or paddle (merchant of record). the checkout hrefs are configuration at the app root — never code, never a committed key.",
|
|
118
|
+
// the plan prose of the pricing cards and the intro plans zone (the
|
|
119
|
+
// billing values — ids, prices, hrefs — stay in checkoutlinks.ts; the
|
|
120
|
+
// words ride here so the store answers every plan sentence)
|
|
121
|
+
"pricing.plan.free.name": "Free",
|
|
122
|
+
"pricing.plan.free.per": "month",
|
|
123
|
+
"pricing.plan.free.blurb": "every app's core surface, for as long as the family is up.",
|
|
124
|
+
"pricing.plan.free.feat.1": "the core surface of all ten applications",
|
|
125
|
+
"pricing.plan.free.feat.2": "no ads inside the apps, ever",
|
|
126
|
+
"pricing.plan.free.feat.3": "community support through the public issue tracker",
|
|
127
|
+
"pricing.plan.pro.name": "Pro",
|
|
128
|
+
"pricing.plan.pro.per": "month",
|
|
129
|
+
"pricing.plan.pro.blurb": "one app and its pro features, billed per application.",
|
|
130
|
+
"pricing.plan.pro.feat.1": "the pro features of one chosen application",
|
|
131
|
+
"pricing.plan.pro.feat.2": "priority routes on the shared gateway",
|
|
132
|
+
"pricing.plan.pro.feat.3": "early access to that app's new surfaces",
|
|
133
|
+
"pricing.plan.studio.name": "Studio",
|
|
134
|
+
"pricing.plan.studio.per": "month",
|
|
135
|
+
"pricing.plan.studio.blurb": "the whole family at once, for people who live in the tools.",
|
|
136
|
+
"pricing.plan.studio.feat.1": "the pro features of every application in the family",
|
|
137
|
+
"pricing.plan.studio.feat.2": "early access across the family",
|
|
138
|
+
"pricing.plan.studio.feat.3": "a direct line to the maintainer for feature requests",
|
|
139
|
+
// the support page anchor (the channels)
|
|
140
|
+
"support.action.notConfigured": "link not configured yet",
|
|
141
|
+
"support.aria.hero": "cobaia support",
|
|
142
|
+
"support.eyebrow": "support",
|
|
143
|
+
"support.title": "Keep the family independent.",
|
|
144
|
+
"support.lede":
|
|
145
|
+
"the family runs on about us$15 a month of domains and free hosting. support covers that floor, keeps every tool free to use and keeps the applications free of ads. both channels below charge zero platform fees, so the support goes where it says.",
|
|
146
|
+
"support.section.channelsAria": "the support channels",
|
|
147
|
+
"support.channel.sponsors.name": "GitHub Sponsors",
|
|
148
|
+
"support.channel.sponsors.promise":
|
|
149
|
+
"sponsor the maintainer through github. zero platform fee, payouts land automatically, and the sponsorship shows up next to the public work it funds.",
|
|
150
|
+
"support.channel.sponsors.cta": "sponsor on github",
|
|
151
|
+
"support.channel.kofi.name": "Ko-fi",
|
|
152
|
+
"support.channel.kofi.promise":
|
|
153
|
+
"one-off donations and monthly memberships through ko-fi. zero fee on donations, no account required on your side beyond the payment.",
|
|
154
|
+
"support.channel.kofi.cta": "buy a coffee",
|
|
155
|
+
"support.section.otherAria": "other ways to help",
|
|
156
|
+
"support.section.otherTitle": "Other ways to help",
|
|
157
|
+
"support.section.otherLede":
|
|
158
|
+
"use the tools and tell the truth about them — report what breaks in the public issue tracker, write about what the family does well or badly, and link the directory when a tool earns it. none of it costs anything and all of it compounds.",
|
|
159
|
+
"support.section.otherNote":
|
|
160
|
+
"the honest line: no ad clicking, no traffic exchange, no bots. automated traffic against an ad surface is invalid traffic — it would end the ad account and the domain reputation. the family does not do it.",
|
|
161
|
+
// the blog page anchor (the update blog)
|
|
162
|
+
"blog.aria.hero": "the family update blog",
|
|
163
|
+
"blog.eyebrow": "updates",
|
|
164
|
+
"blog.title": "What the family is building.",
|
|
165
|
+
"blog.lede":
|
|
166
|
+
"short, written updates on the family — how the sites are built, what they cost and why they ship the way they do. every post lives on this page; the index jumps to it.",
|
|
167
|
+
"blog.section.indexAria": "the post index",
|
|
168
|
+
"blog.section.fullAria": "the posts in full",
|
|
169
|
+
"blog.post.meta": "{date} · {words} words · {tags}",
|
|
170
|
+
// the legal page anchor (privacy and terms)
|
|
171
|
+
"legal.aria.hero": "cobaia legal",
|
|
172
|
+
"legal.eyebrow": "legal",
|
|
173
|
+
"legal.title": "Privacy and terms.",
|
|
174
|
+
"legal.lede":
|
|
175
|
+
"the short version: the family sites are static files, they collect nothing by default, they track nobody, and the details below are the long version of the same promise. last updated 2026-10-10.",
|
|
176
|
+
"legal.privacy.aria": "privacy policy",
|
|
177
|
+
"legal.privacy.title": "Privacy policy",
|
|
178
|
+
"legal.privacy.collect.head": "What we collect: nothing, by default.",
|
|
179
|
+
"legal.privacy.collect.text":
|
|
180
|
+
"The family sites are static files served from a CDN. They set no cookies, run no analytics, embed no fingerprinting and write nothing to your browser — no local storage, no indexed databases, no session records. The only thing a visitor machine does is load the interface.",
|
|
181
|
+
"legal.privacy.logs.head": "Server logs.",
|
|
182
|
+
"legal.privacy.logs.text":
|
|
183
|
+
"The hosting providers (netlify, vercel, cloudflare and equivalents) keep standard access logs on their own infrastructure for security and abuse prevention. The family does not add identifiers to them and does not export them for profiling.",
|
|
184
|
+
"legal.privacy.ads.head": "Advertising.",
|
|
185
|
+
"legal.privacy.ads.text":
|
|
186
|
+
"The family site runs reserved, visibly labeled ad containers on its content pages. No ad script ships in the code: the advertising partner's loader is only added after the partner approves the site, and it arrives through the deploy environment. Once live, third-party vendors including Google may serve ads and use cookies to personalize them, as governed by the partner's own policies. You can opt out of personalized advertising through your ad settings (for Google, adssettings.google.com). Ads never appear inside the applications themselves.",
|
|
187
|
+
"legal.privacy.analytics.head": "Analytics.",
|
|
188
|
+
"legal.privacy.analytics.text":
|
|
189
|
+
"There is no analytics on the family sites today. If that ever changes, it will be opt-in, minimal and disclosed on this page before it goes live.",
|
|
190
|
+
"legal.privacy.payments.head": "Payments.",
|
|
191
|
+
"legal.privacy.payments.text":
|
|
192
|
+
"When paid plans go live, checkout runs on the payment provider's own pages (stripe, paddle or equivalents). The family sites never see or store card data.",
|
|
193
|
+
"legal.privacy.contact.head": "Contact.",
|
|
194
|
+
"legal.privacy.contact.text":
|
|
195
|
+
"Privacy questions and data requests go through the public issue tracker of the family repository — the same channel the software itself uses.",
|
|
196
|
+
"legal.terms.aria": "terms of use",
|
|
197
|
+
"legal.terms.title": "Terms of use",
|
|
198
|
+
"legal.terms.service.head": "The service.",
|
|
199
|
+
"legal.terms.service.text":
|
|
200
|
+
"The family applications and this site are provided as is, without warranty of any kind. They are tools built in public: features change, break and get fixed in the open. Nothing here is offered as professional advice or with any availability guarantee.",
|
|
201
|
+
"legal.terms.use.head": "Acceptable use.",
|
|
202
|
+
"legal.terms.use.text":
|
|
203
|
+
"Use the tools like a person. Do not run automated traffic against the sites or, above all, against any advertising surface: clicking your own ads, exchanging traffic or driving bots at ad containers is invalid traffic — it defrauds the advertisers, it ends the ad account and it burns the domain reputation. The family does not do it and asks nobody else to.",
|
|
204
|
+
"legal.terms.data.head": "Your data.",
|
|
205
|
+
"legal.terms.data.text":
|
|
206
|
+
"Since the sites collect nothing, there is nothing to export or delete. What you do inside the hosted tools stays governed by the tool's own page.",
|
|
207
|
+
"legal.terms.ip.head": "Intellectual property.",
|
|
208
|
+
"legal.terms.ip.text":
|
|
209
|
+
'The family code is free software under its repository license; the name devthink and the family marks belong to their author. The site name "cobaia" is provisional and may change.',
|
|
210
|
+
"legal.terms.changes.head": "Changes.",
|
|
211
|
+
"legal.terms.changes.text":
|
|
212
|
+
"These terms change when the facts change, and the changes land on this page with a new date. Using the sites after a change means you accept it.",
|
|
213
|
+
// the notfound page anchor (the empty address)
|
|
214
|
+
"notfound.eyebrow": "404",
|
|
215
|
+
"notfound.title": "This address is not part of the family.",
|
|
216
|
+
"notfound.lede":
|
|
217
|
+
"the page you asked for does not exist here. the front door and the app directory are the honest way back in.",
|
|
218
|
+
"notfound.action.home": "back to the front door",
|
|
219
|
+
"notfound.action.apps": "browse the apps",
|
|
220
|
+
// the intro page anchor (the bare presentation — the family grammar's last
|
|
221
|
+
// seat: every app carries an intro; the cobaia one presents the real offer,
|
|
222
|
+
// never invented numbers, never fake testimonials)
|
|
223
|
+
"intro.aria.page": "cobaia — the intro",
|
|
224
|
+
"intro.aria.splash": "the cobaia intro splash",
|
|
225
|
+
"intro.splash.name": "cobaia",
|
|
226
|
+
"intro.splash.role": "the devthink family site",
|
|
227
|
+
"intro.splash.skip": "click, tap or any key to skip",
|
|
228
|
+
"intro.bar.aria": "the intro doors",
|
|
229
|
+
"intro.hero.eyebrow": "the presentation",
|
|
230
|
+
"intro.hero.title": "The family's front door, and its price list.",
|
|
231
|
+
"intro.hero.lede":
|
|
232
|
+
"cobaia is the monetization face of the devthink family, kept apart so the ten applications stay clean — no ads inside the apps. the directory, the plans, the support channels and the update blog live here, on one static site that collects nothing by default.",
|
|
233
|
+
"intro.hero.cta": "enter the site",
|
|
234
|
+
"intro.hero.cue": "see the plans",
|
|
235
|
+
"intro.hero.note":
|
|
236
|
+
"the name is provisional, the hosting is free and static, the storage is zero — the long version of that promise rides the legal page.",
|
|
237
|
+
"intro.plans.aria": "the plans as they really are",
|
|
238
|
+
"intro.plans.eyebrow": "the plans",
|
|
239
|
+
"intro.plans.title": "The plans, as they really are.",
|
|
240
|
+
"intro.plans.lede":
|
|
241
|
+
"the same three plans the pricing page carries — us$0, 5 and 19 a month, placeholder prices until billing lands. a plan activates the moment its checkout link is configured, and says so honestly until then.",
|
|
242
|
+
"intro.plans.choose": "choose {name}",
|
|
243
|
+
"intro.plans.notConfigured": "checkout not configured yet",
|
|
244
|
+
"intro.plans.door": "the full pricing page",
|
|
245
|
+
"intro.doors.aria": "the doors of the site",
|
|
246
|
+
"intro.doors.eyebrow": "the doors",
|
|
247
|
+
"intro.doors.title": "Where the family lives.",
|
|
248
|
+
"intro.doors.apps": "the app directory",
|
|
249
|
+
"intro.doors.appsText":
|
|
250
|
+
"ten apps live beside this site. the full directory, with the honest role each application declares for itself, is one door away.",
|
|
251
|
+
"intro.doors.appsCta": "browse the ten apps",
|
|
252
|
+
"intro.doors.legal": "privacy and terms",
|
|
253
|
+
"intro.doors.legalText":
|
|
254
|
+
"the family sites are static files: they collect nothing by default and they track nobody. the privacy policy and the terms of use are the long version of the same promise.",
|
|
255
|
+
"intro.doors.legalCta": "read the legal page",
|
|
256
|
+
"intro.footer.siteAria": "the site doors",
|
|
257
|
+
"intro.footer.familyAria": "the family row",
|
|
258
|
+
"intro.footer.family": "the family",
|
|
259
|
+
"intro.footer.memberTip": "open {slug} — the door beside this site",
|
|
260
|
+
// the ad slot widget (the reserved box of the content pages)
|
|
261
|
+
"ads.slot.reserved": "reserved · {format}",
|
|
262
|
+
} as const;
|
|
263
|
+
|
|
264
|
+
export type LabelKey = keyof typeof LABEL_MANIFEST & string;
|
|
265
|
+
|
|
266
|
+
/** the store mechanism rides the vault factory (the storage law of the
|
|
267
|
+
* family: the vault owns the store, the app owns the manifest — the
|
|
268
|
+
* mechanism is imported, never duplicated). the surface answers its own no-storage law: the visitor browser never writes. */
|
|
269
|
+
const store = createLabelStore({
|
|
270
|
+
manifest: LABEL_MANIFEST,
|
|
271
|
+
revision: LABEL_REVISION,
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
/** the typed accessor of the copy: the only door a widget or page anchor uses. */
|
|
275
|
+
export function label(key: LabelKey): string {
|
|
276
|
+
return store.label(key);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/** the placeholder door: fills the {token} slots of one format key with the
|
|
280
|
+
* runtime values (the store keeps the sentence, the widget passes the data). */
|
|
281
|
+
export function labelfill(key: LabelKey, values: Record<string, string>): string {
|
|
282
|
+
return store.labelfill(key, values);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/** the seed ingests the manifest into the labels store, idempotent, and the
|
|
286
|
+
* translated rows stay protected from the overwrite. in the browser the rows
|
|
287
|
+
* ride the local cache of the site db; on the server db.ts seeds the same
|
|
288
|
+
* manifest into the labels table through seedLabelRows. */
|
|
289
|
+
export function seedLabels(): Promise<{ seeded: number; protected: number }> {
|
|
290
|
+
return store.seedLabels();
|
|
291
|
+
}
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "cobaia",
|
|
3
|
+
"short_name": "cobaia",
|
|
4
|
+
"description": "The front door of the DevThink family: pricing, support, the app directory and the update blog for ten deployable web applications.",
|
|
5
|
+
"id": "/",
|
|
6
|
+
"start_url": "/",
|
|
7
|
+
"scope": "/",
|
|
8
|
+
"display": "standalone",
|
|
9
|
+
"display_override": ["window-controls-overlay", "standalone", "minimal-ui"],
|
|
10
|
+
"orientation": "any",
|
|
11
|
+
"background_color": "#0a0a0b",
|
|
12
|
+
"theme_color": "#f59e0b",
|
|
13
|
+
"lang": "en",
|
|
14
|
+
"dir": "ltr",
|
|
15
|
+
"categories": ["developer", "productivity"],
|
|
16
|
+
"icons": [
|
|
17
|
+
{
|
|
18
|
+
"src": "./favicon.svg",
|
|
19
|
+
"sizes": "any",
|
|
20
|
+
"type": "image/svg+xml",
|
|
21
|
+
"purpose": "any"
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
"src": "./favicon.svg",
|
|
25
|
+
"sizes": "any",
|
|
26
|
+
"type": "image/svg+xml",
|
|
27
|
+
"purpose": "any maskable"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"src": "./icon.svg",
|
|
31
|
+
"sizes": "any",
|
|
32
|
+
"type": "image/svg+xml",
|
|
33
|
+
"purpose": "any"
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
"src": "./icon.svg",
|
|
37
|
+
"sizes": "any",
|
|
38
|
+
"type": "image/svg+xml",
|
|
39
|
+
"purpose": "any maskable"
|
|
40
|
+
}
|
|
41
|
+
],
|
|
42
|
+
"shortcuts": [
|
|
43
|
+
{
|
|
44
|
+
"name": "Pricing",
|
|
45
|
+
"short_name": "Pricing",
|
|
46
|
+
"url": "/pricing"
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
"name": "App directory",
|
|
50
|
+
"short_name": "Apps",
|
|
51
|
+
"url": "/apps"
|
|
52
|
+
}
|
|
53
|
+
],
|
|
54
|
+
"related_applications": [],
|
|
55
|
+
"prefer_related_applications": false
|
|
56
|
+
}
|
package/mime.types
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# MIME types served by the site
|
|
2
|
+
text/html html htm
|
|
3
|
+
text/css css
|
|
4
|
+
text/javascript js mjs
|
|
5
|
+
application/json json
|
|
6
|
+
application/manifest+json webmanifest
|
|
7
|
+
image/svg+xml svg
|
|
8
|
+
image/png png
|
|
9
|
+
image/jpeg jpg jpeg
|
|
10
|
+
image/x-icon ico
|
|
11
|
+
font/woff2 woff2
|
|
12
|
+
application/wasm wasm
|
package/netlify.toml
ADDED