Survival LLM
A free AI assistant and survival library (1.5 million offline articles) that runs on your own computer, with no internet. Shows the source of every answer. Mac and Windows.
翻译准备中
尚未检测到更新。GitHub 发布版本后会显示在这里。
个人 GitHub 账号下公开仓库的所有者可以登录并验证所有权,自行管理介绍,无需管理员批准。
登录并生成验证文件,在 30 分钟内将其提交到此仓库的默认分支。其他分支或分叉仓库中的文件无效。然后在下方检查文件,即可无需管理员批准直接管理项目。请保留文件以维持管理权限。
如需登录,将前往登录页面。登录后请回到此处,再次点击所选的验证按钮。
在项目文件夹中运行 /nomorevibe verify,即可验证所有权并自行管理介绍。
一分钟完成验证leonardoramirezr/leo-os
翻译准备中
取自仓库 README
正在加载完整 README。 以下为已保存的摘录。
A collection of static web apps published together on GitHub Pages. The home screen mimics an
phone home screen: every app is an icon.
Every folder inside apps/ is an app and is published at / /. The home screen
discovers them at build time, so there is nowhere else to register them.
• The folder name is part of the URL: lowercase letters, digits and d查看完整 README ↗A collection of static web apps published together on GitHub Pages. The home screen mimics an phone home screen: every app is an icon.
.
├── home/ # Home screen (SvelteKit + Svelte 5)
├── apps/
│ └── willchat/ # One folder per app
│ ├── app.json # Manifest: { "name": "WillChat" }
│ ├── icon.svg # The app's icon
│ └── …
├── shared/ # The account and the database, used by the home screen and every app
├── db/
│ ├── schema.ts # The models: the tables and their row level security policies
│ ├── migrations/ # Generated from the models; the deploy applies them
│ ├── preview.mjs # Gives each preview a copy of the database of its own
│ └── storage.mjs # Gets Leo Partī's bucket ready (see «Leo Partī»)
├── neon/
│ └── auth-proxy.ts # Stands Neon Auth on an own domain (see «Own domain»)
├── scripts/
│ ├── build.mjs # Builds the home screen and every app into dist/
│ ├── icons.mjs # Turns each icon.svg into the PNG iOS asks for
│ ├── preview.mjs # Serves dist/ the way GitHub Pages does
│ ├── preview-slug.sh # The folder a branch gets inside previews/
│ ├── previews-index.mjs # Builds the list of published previews
│ └── publish-pages.sh # Writes the site (or a preview) into gh-pages
└── .github/workflows/
├── deploy.yml # Publishes on every push
└── preview-cleanup.yml # Drops the preview when the branch is deleted
Every folder inside apps/ is an app and is published at <BASE_PATH>/<folder>/. The home screen
discovers them at build time, so there is nowhere else to register them.
| File | What it must hold |
|---|---|
app.json | { "name": "Visible name" } |
icon.svg | A square, full-bleed icon with no rounded corners: the home screen and iOS apply the mask. |
package.json | A build script that writes build/index.html using the BASE_PATH environment variable as its base path. |
On top of that:
icon.svg: there is no second drawing to make.localStorage and IndexedDB too. Use a prefix of your own in the keys (e.g. willchat:).light-dark(light, dark) under
color-scheme: light dark, not in an @media (prefers-color-scheme) block: that is how the theme
picked in the home screen's Ajustes reaches the app.For a new SvelteKit app, start from pnpm dlx sv create apps/<folder> --template minimal --types ts --add sveltekit-adapter="adapter:static"
and copy two details from apps/willchat: paths.base read from BASE_PATH in vite.config.ts,
and ssr = false + prerender = true in src/routes/+layout.ts.
Requires Node 24+ and pnpm.
pnpm install
cp .env.example .env # where Neon is; without it every screen says there is no database
pnpm --filter willchat dev # one app
pnpm --filter home dev # the home screen
pnpm check # svelte-check across every project, and the models against the migrations
pnpm icons # regenerates the apple-touch-icon.png files after editing an icon.svg
pnpm db:generate # writes the migration for what changed in db/schema.ts
pnpm db:migrate # applies the pending migrations to DATABASE_URL, and tells the Data API
pnpm db:preview create <name> # a preview's own schema: public copied once, then this branch's migrations
To try the whole site the way it is published:
BASE_PATH=/apps pnpm build
BASE_PATH=/apps pnpm preview # http://localhost:4173/apps/
Everything an app records — who owes what, which models WillChat uses — lives in one Neon Postgres database and is queried straight from the browser. There is still no backend of ours in between: Neon Auth holds the session and the Neon Data API (PostgREST over HTTPS) serves the tables. The one exception is on an own domain, where a function that only forwards stands Neon Auth on that domain.
rememberMe, which makes the cookie outlive closing the tab; how long it may live is the
session lifetime configured in the Neon console.db/schema.ts only ever let auth.user_id() — the account behind the request's
token — see its own. Signed out, the Data API sees the anonymous token Neon Auth hands anybody
and runs it as the anonymous role, which reaches no table: all it may do is open a Leo Partī post
whose link it has, or an account's bio (Leo Partī).localStorage and IndexedDB. Their keys carry the
account too: two people using the same phone do not see each other's, and signing out drops the
lot. A Leo Partī post's photos and videos are the exception: they are meant to be seen on other
devices, so they go to a bucket of the project's Object Storage, which the database signs the way
into (Leo Partī).shared/ holds all of this — the two clients, the sign-in screen, the account panel — and is a
workspace package every project depends on.
db/schema.ts is the models: the one place a table is described. The row types the browser works
in come from it too, so a column is spelled once and the apps stop typechecking if it moves.
A change to it is a change in two steps:
# 1. Edit db/schema.ts, then write the SQL for what changed
pnpm db:generate
# 2. Read the migration it wrote, commit it next to the models
git add db/migrations
pnpm check fails if you skip the first step — and writes the missing migration while it is at it,
so the fix is to read what it left in db/migrations/ and commit that.
Applying them is the deploy's job: deploy.yml runs pnpm db:migrate on the default branch,
before building. Each migration runs once, in order, and the database remembers which ones it has
seen. To apply them by hand, put the connection string in DATABASE_URL and run pnpm db:migrate.
Then it tells the Data API to read the tables again. The Data API answers from what it last read of
them, and only reads them again when told or once its cache runs out: until then, the site would
find the new columns missing. Telling it takes NEON_API_KEY and NEON_PROJECT_ID (Setting it
up); without them pnpm db:migrate says so, and leaves it to the cache. With them,
a run that cannot tell it fails before the site is published, and the next one tells it again.
Only the default branch migrates public. The migrations are one line of history, and two branches
applying their own would tangle it. A branch's migrations are tried in its preview instead, on a copy
of the database of its own (Previews), so a new column works in the preview before the
branch is merged.
Two things drizzle-kit does not track, and db/migrations/0001_grants.sql does by hand: which
roles may reach a table at all, and that a table a later migration creates inherits the same. That
last part is what keeps a new model from needing anything added there.
In the Neon console, on the project this site uses:
…/auth). Under its configuration, add
https://leonardoramirezr.github.io as a trusted domain — previews live on the same origin, so
one entry covers them all — and set the session lifetime to a year. Under Sign-up with
Email, turn on Verify at Sign-up with Verification code: the sign-in screen asks for a
code, not a link. Signing in with an email still unconfirmed only emails a fresh code if sending
on sign-in is on as well, which the Neon CLI does with
neon neon-auth config email-password update --send-verification-email-on-sign-in; without it
the screen still asks, and «Enviar otro código» is what sends one.…/rest/v1). If it asks which origins may call it,
that same one. Do this before the first migration: the authenticated and anonymous roles the
policies name are its, and turning it on is what creates them.DATABASE_URL=… pnpm db:migrate.Then, in this repository under Settings → Secrets and variables → Actions:
| Name | What | |
|---|---|---|
| Variables | NEON_AUTH_URL | The Auth URL from step 1 |
| Variables | NEON_DATA_API_URL | The Data API URL from step 2 |
| Variables | NEON_PROJECT_ID | The project's ID, from its settings in the console |
| Secrets | DATABASE_URL | The project's connection string, for the migrations and the previews |
| Secrets | NEON_API_KEY | A Neon API key, to tell the Data API what to serve and when to look again, and to make Leo Partī's bucket |
| Variables | LEOGRAM_BUCKET | Optional: the name of Leo Partī's bucket, leogram if unset |
| Secrets | LEOGRAM_STORAGE_ACCESS_KEY_ID, LEOGRAM_STORAGE_SECRET_ACCESS_KEY | Optional: a key of your own to that bucket; without them the deploy makes one |
The two URLs are not secrets: they are the public addresses of services that decide for themselves
what the caller may see, and they end up in the published JavaScript either way. The connection
string and the API key are: the first opens the whole database with none of the policies in the way,
the second whatever the key reaches — a project-scoped key (organization Settings → API keys)
keeps that to this project. The default branch's run uses both to migrate public and have the Data
API read it again; a preview's, to make its own schema, and all it does with public is read it.
For pnpm dev, pnpm build and the pnpm db:* commands, the same values go in a .env at the
root — see .env.example.
A build with no Neon URLs still builds and runs, and every screen says there is no database.
Neon Auth lives at *.neon.tech, so to a site on github.io its session cookie is a third-party
one. Safari in a tab may let it through; a web app saved to the iPhone home screen never does, and
there signing in goes through and the very next call is told the session ran out. The way out is to
serve the site and Neon Auth from the same site: the site on a domain of its own, Neon Auth on a
subdomain of it. Neon Auth cannot be given a domain, so a Neon Function
that only forwards to it, neon/auth-proxy.ts, takes that subdomain instead. The Data API stays as
it is: it is shown a token, not a cookie, and answers any origin.
With a free subdomain from Open Domains, say leo-os.is-cool.dev — pick
one of its domains on the Public Suffix List (is-cool.dev,
is-not-a.dev, localplayer.dev, is-local.org, is-a-fullstack.dev), so that the subdomain is a
site of its own and not shared with everyone else's:
The site. At Open Domains, a CNAME from leo-os.is-cool.dev to leonardoramirezr.github.io,
DNS only (not proxied). Then the repository variable PAGES_DOMAIN = leo-os.is-cool.dev and a
run of deploy.yml on main (Actions → Deploy to GitHub Pages → Run workflow): the site is
built for the domain's root and ships the CNAME file GitHub Pages reads its domain from — the
variable, not the field in Settings → Pages, is what decides, since every publish replaces
that file. Once the certificate is issued, tick Enforce HTTPS there. github.io/leo-os/
redirects to the domain from then on; previews published before need a push to move with it.
The function. With the Neon CLI, linked to this project and its production branch:
neon functions deploy authproxy --src neon/auth-proxy.ts \
--env NEON_AUTH_ORIGIN=https://ep-xxx.neonauth.c-7.us-east-2.aws.neon.tech \
--env SITE_ORIGIN=https://leo-os.is-cool.dev
neon functions domains register auth.leo-os.is-cool.dev --slug authproxy --output json
NEON_AUTH_ORIGIN is the Auth URL without its path. The second command answers with a
cname_target.
Its subdomain. At Open Domains, a CNAME from auth.leo-os.is-cool.dev to that target, DNS
only too. neon functions domains list --output json says active once the certificate is
issued; https://auth.leo-os.is-cool.dev/<database>/auth/get-session then answers null.
Neon Auth. Add https://leo-os.is-cool.dev to its trusted domains.
This repository. The variable NEON_AUTH_URL becomes the function's domain with the Auth
URL's path: https://auth.leo-os.is-cool.dev/<database>/auth. Run deploy.yml on main again.
On the iPhone, the old icon goes and the new address is added again. Whatever stays on the device
only — the wallpaper, WillChat's conversation — belongs to the old origin and does not come along.
Safari caps the cookies of a subdomain that points at another provider to seven days, renewed each
time the session is, so an app left unopened for a week asks to sign in again. For pnpm dev,
.env keeps the Auth URL as it is: the function only answers the published site.
Everything is published to the gh-pages branch, which is the only thing GitHub Pages serves:
| What is pushed | Where it lands | URL |
|---|---|---|
main | the root of gh-pages | …github.io/leo-os/ |
| any other branch | previews/<branch>/ | …github.io/leo-os/previews/<branch>/ |
On an own domain, the same without /leo-os: https://<domain>/ and
https://<domain>/previews/<branch>/.
deploy.yml runs on every push: it passes pnpm check, brings the database up to date if this is
the default branch (or gives a preview its own copy of it), builds with the base path it is due and
scripts/publish-pages.sh writes the result into gh-pages. Publishing the site does not wipe the
previews, and each branch only touches its own folder; if two publish at once, the script reads the
branch again and retries.
The very first time, and in this order: first a push to main, which is what writes the site to the
root of gh-pages; then, in the repository, Settings → Pages → Build and deployment → Source:
Deploy from a branch, choosing the gh-pages branch with the / (root) folder. The other way
round, the site stays a 404 until the next push to main. As long as that setting is left alone,
Pages keeps serving the last deploy made with the previous option («GitHub Actions») and none of
this shows up published.
Every branch other than main is published on its own, so a change can be opened and tried before
it is merged.
claude/wizardly-euler is served at /leo-os/previews/claude-wizardly-euler/.…/leo-os/previews/ lists the ones that exist, newest to oldest.preview-cleanup.yml drops its folder, its schema and what it
uploaded to Leo Partī's bucket. Once none are left, previews/ disappears.
GitHub runs that workflow from main, so the cleanup starts working once the file lands there.Each preview has a database of its own: a schema in the same Neon database, named after it
(preview_claude_wizardly_euler), which db/preview.mjs keeps up to date. A push that finds none
— the branch's first, or the next one after a run that did not get to finish — makes it a copy of
public as it is at that moment (tables, rows, functions, policies and grants), with the branch's
own migrations applied on top. Every push after that keeps it, rows included, and only
applies the migrations it brings: a branch that adds a column can be tried before it is merged,
with whatever was typed into the preview still there. The Data API serves every preview's schema
next to public; the preview's queries name theirs, and the published site's name none, which keeps
them on public.
drizzle.<schema>, next to public's.
A migration counts as new until the schema has it, whatever its date: the ones main brings over
when it is merged into the branch are applied too.public again, and the run summary says why.public has is refused: drizzle only applies
what is newer, so pnpm db:migrate would skip it on main for good. Generating it again, once
main is merged into the branch, puts it last.pnpm db:preview drop <name>, or from the Neon
console — and push: the next push copies public again.public is only read: a migration that fails leaves the schema
as the push before left it, and the run fails with it. A migration may name public the way
drizzle-kit writes it, which the preview points at its own schema, but not change the
search_path.NEON_API_KEY and NEON_PROJECT_ID (Setting it up): the Data API is
told what to serve through the Neon API. Without them the preview uses the published data, as the
run summary says.A preview lives on the same origin as the published site, so it shares the session, localStorage
and IndexedDB with it: it opens already signed in, with the same wallpaper and the same WillChat
conversation, which never reach the database. The copy of its rows each app keeps on the device is
the preview's own.
Safari will not take an SVG for the icon saved with «Add to Home Screen»: without a PNG, it saves a
screenshot of the page instead. That is why scripts/icons.mjs turns each icon.svg into a
180 × 180 apple-touch-icon.png inside the project's static/, and each app.html links it with
<link rel="apple-touch-icon">. The PNG is generated at build and install time; it is not
versioned, so the SVG stays the only source.
The icon has to be opaque and reach the edges: iOS applies its own rounded mask and paints anything transparent black. Safari also caches the icon eagerly; if the old one keeps showing up while testing, close the tab and open the page again.
The home screen, and every page that shows an icon, draws the SVG itself in an <img>. There,
Safari renders whatever goes through an SVG filter or mask at low resolution, which comes out
blurry on an iPhone's 3x screen. So nothing in an icon that should look sharp goes through either:
a drop shadow or a glow is a blurred copy of the shape, drawn underneath it, and a cut-out is a
clipPath.
Besides the published apps, the home screen carries three icons of its own. They live in the dock at the bottom, which stays put whichever page of apps is showing, and go without their names there, as on iOS:
Added to the home screen, the dock floats 12 points over the bottom edge, as the iOS 26 one does.
In Ajustes:
localStorage under home:wallpaper, one per account. With no photo, the
default gradient is used, which comes back on «Quitar».leo-os:theme: each project writes its colours with
light-dark(), and Account puts the theme picked on <html> as data-theme.#1C1446, 28, 20, 70), and previewed at the top
of that page. It is kept in the account as home:status-bar-color; «Usar el predeterminado» goes
back to the site's own. The band takes it once Ajustes closes — while it is open, the band keeps
the colour it had — and on opening, once the session is confirmed: until then it is the purple of
the door.iOS 26 does not colour that band with theme-color. WebKit takes the plain background-color of the
fixed element at the top edge, and reads it again whenever it changes only while that element is
shorter than the screen: one that fills it — the door Account shows while loading, the home
screen, an app's frame — keeps whatever colour the band already had, so the door's purple would stay
for good. That is why Account keeps an invisible strip along the top edge, above everything, in
the project's --status-bar, or else its --bg: the home screen sets the first to the colour
picked here, and in every app the band is the page's own background, following the theme.
A ChatGPT-style chat for creating and editing images with the OpenAI API and your own API key.
api.openai.com. Keeping it there is what saves entering it again on every device./v1/models, and
any ID can also be typed in.input_image. Every turn sends the whole
conversation, generated images included, so the model can keep editing them.whisper-large-v3, the same model as Lista, with the same Groq API key:
the one saved as groq:api-key, which every app here that uses Groq reads, so a key entered in
Lista already works here. Without one, the microphone opens the settings to enter it. Whisper is
told to expect the app's own language, Spanish or English, and the segments it doubts were speech
are dropped, as in Lista. The key and the recordings are only ever sent to api.groq.com.background: true and are polled every 2 s. Generating an image can take more than a
minute and Safari on iOS cuts off requests that go 60 s without a response; this way the answer is
also recovered if you reload or switch apps.[[[question]]] written in it
becomes a text field (the same question twice is one field), and fields for images or files can be
added on top, each with its title. Sending it gives one result, text or images, and no
conversation: the prompt goes as the system prompt with each answer on the lines right after its
[[[question]]], and the files follow in the user's message, each after its field's title.
Images go as input_image; text files (.txt, .md, .csv, .json…) as their text; anything
else, a PDF for instance, as input_file, up to 25 MB.willchat_mini_apps, so they are on every device. What was
answered, the files and the last result stay in IndexedDB, one entry per mini app, like the
conversation: the result is polled the same way, and is picked up again after a reload.A ledger of who owes you money: opening it shows how much you are owed in total, how much of that is already overdue, the list of people who owe, and two buttons at the bottom.
me_deben_people and me_deben_movements tables, with
a copy on the device so the app opens without waiting. Amounts are stored as whole cents so
balances do not accumulate rounding errors. What an earlier version left under the
keys of this browser is brought over the first time you sign in, and only into an empty ledger.A checklist that is only ever edited by voice: dictate a list and every thing in it becomes an item with a box to tick, then say what to change.
whisper-large-v3 turns the recording into text, and
openai/gpt-oss-120b decides what that text does to the list. The model is shown the list
numbered as it is on screen and can only answer with actions — add, edit, remove, check, uncheck,
clear, undo — that Groq's strict mode holds to a JSON schema. An action pointing at an item that
is not there is dropped.api.groq.com, together with the recordings. It is saved as
groq:api-key, with no app's prefix: every app here that uses Groq reads that same one.lista_items table, one row per item, with a copy on the device so the app
opens without waiting. Coming back to the app reads it again, in case it changed on another device.Flashcards studied with spaced repetition, the way Anki does it: decks of cards, each card shown again just before it would be forgotten.
src/lib/schedule.ts. A
new card is learned in steps of one and ten minutes and comes back the next day, or in four days
with «Fácil». From then on «Bien» multiplies the interval by the card's ease, which starts at
2.5; «Difícil» lowers the ease and «Fácil» raises it. A forgotten card is relearned in ten minutes
and starts over at a day. The day turns over at 4 a.m., so a session past midnight still belongs
to the day before.Voice to text: say something and it is written down, then go on dictating, or say what to change.
src/lib/diff.ts): the text as it is now, with every word taken out in red and
struck through, where it was, and every word put in, in green. It compares what the model was
given with the text on screen, so whatever was dictated or typed afterwards shows too, and the
view says so. It is there for as long as a change of the model's is behind the text: a text only
dictated and typed, or emptied since, has none. Like the versions it compares, it lasts as long as
the visit.A parody of Instagram: carousel posts with a song, each with a link that opens for anybody, signed in or not, or only for some friends; and a bio that lists the ones its author picks.
It used to be Leogram, at …/leogram/, and links to there still open it: the build leaves a page at
the old path that forwards to …/leo-parti/, query and all (scripts/build.mjs). What is stored
kept the old name, since renaming it would mean moving every row and file: the tables and functions
(leogram_*, leogram_private), the bucket, its credential and its variables (LEOGRAM_*), and
the caches in the browser.
It works like Instagram but does not look like it: it is printed as a fanzine off a risograph. Two
inks on paper, cobalt with a lime one a hair off it, lilac stickers, thick outlines and hard shadows;
in the dark the paper goes black and the lime leads. The type is Bricolage Grotesque and Space Mono,
which travel with the app (src/lib/fonts, under the SIL Open Font License) rather than coming from
a font service. The icon is Leo's sign, ♌, printed the same way.
src/lib/text.ts lays a text out once at 1080 pixels and draws it on a canvas, both into the
photo and over it in the editor and the composer, only scaled, so what shows is what goes up,
line breaks included; while a text is typed, CSS draws it with the same font, size and room.The deploy sets it up on its own: on every push, after the migrations, db/storage.mjs makes the
bucket if it is not there (private, named leogram unless LEOGRAM_BUCKET says otherwise), hands
the database the key it signs with, sets the bucket's CORS rules so that pages can upload, and
deletes the files nothing points to any more. Nothing has to be done in the console, and the only
thing it needs is what the previews already need: DATABASE_URL, NEON_API_KEY,
NEON_PROJECT_ID and NEON_DATA_API_URL (Setting it up). Locally,
pnpm db:storage does the same with the values in .env.
LEOGRAM_STORAGE_ACCESS_KEY_ID and LEOGRAM_STORAGE_SECRET_ACCESS_KEY give
one (a credential with storage:read and storage:write, made in the console or with
neon credentials create), the script makes one of its own on the bucket's branch,
leo-os-leogram, and keeps it in leogram_private.bucket. The Neon API shows a secret only
once: when the database lacks the one it has, or it stops working, the credential is given a new
one, and the database keeps that.public/… for the site, preview_…/… for a preview. A preview's copy of a published post shows
the published files, and deleting it there deletes none of them.Texts rewritten with prompts of one's own: one turns a message into a notice for the pastoral school, another makes it formal, another corrects it — whatever each one's instructions say.
src/lib/diff.ts). Whitespace counts only where it breaks a line, and a line break taken out or
put in shows as a ¶. Two long texts with little in common may take more than a second to compare:
past it, what is left shows as taken out and put in whole./models lists, openai/gpt-oss-120b until
another is picked. It is one for every prompt, and it names them too. It is plain text in and plain
text out, with nothing model-specific sent, so any chat model the key can use will do.groq:api-key, and asks for one when
there is none. Only the key, the text and the prompt's instructions are ever sent to api.groq.com.transforma_prompts table. The text lives in
, one row per account, with its versions: the last 50, as long as together they
stay under 100,000 characters. Pasting the result elsewhere means going to another app, and iOS
often closes this one meanwhile: undo, and what the prompt changed, are still there on coming back,
and on another device. Both have a copy on the device, and the text's, when the database did not
get it, is sent rather than read over the next time the app opens, as in Dictado.A workout routine to follow at the gym: each day's exercises with the effort to beat, a timer for every set and every rest, and how the effort has gone, day by day.
src/lib/effort.ts).whisper-large-v3 until another is picked, there or in Ajustes, told it is Spanish
and given a few gym words to spell) writes it down into the box, after whatever is already there.
«Crear rutina» hands the text to a chat model on Groq — openai/gpt-oss-120b until another is
picked, there or in Ajustes —, shown the catalog and held to a schema whose only exercise ids are
the catalog's, and the editor opens with the routine it wrote: nothing is saved until it has been
looked over there, and «‹ Texto» goes back to the description to say more and try again. The
first time, with no list yet, the editor links to it (src/lib/writer.ts).src/lib/components/ReviseDock.svelte).openai/gpt-oss-120b until another is picked in Ajustes, held to a schema whose only
ids are the catalog's — says which catalog exercise each one is, and nothing is saved until the
matches have been looked over, and changed where they are wrong. Each exercise keeps the name the
JSON gave it; the match gives it its animation. Without a Groq key, or when Groq cannot be
reached, they are matched by name alone. The key is the one Lista, Repaso, Dictado and Transforma
use, , and all is ever sent is the key and, depending on the way in,
the exercises' names, a routine's description or the recording of it.Training programs for a treadmill that has none of its own, or whose programs are not the ones wanted: the app says when to change the speed, as a mid- or high-end treadmill's programs do, and the speed is set by hand.
6,5) is taken as 6.5. The program is drawn as it is typed. One left unnamed is
called «Programa N».caminadora:run), not in the database: iOS may close
the app while it sits in the background, and opening it again finds the program still running,
where the treadmill is, a tap away on the list. One paused for over an hour is let go, and so is
one already over.A stopwatch, a timer and a clock that say the time aloud, on a display like Caminadora's: black and white, the numbers in seven segments, the ones off still faint as on an LCD.
reloj:tab,
on the device). A dot on a key marks the stopwatch or the timer running while another one shows.reloj:timer-duration).reloj:stopwatch-every, reloj:timer-every, reloj:clock-every).reloj:stopwatch, reloj:timer): iOS may close the app in the background, and opening it
again finds them where they would be. A timer that reached zero over a minute before does not ring
then; it shows it is over.Prompts with blanks: written once, filled in and copied every time they are needed.
Escribe un correo para [[[¿A quién va dirigido?]]]. Insertar pregunta puts the brackets around
what is selected, or around a word to type over, since they are a chore to type on a phone. The
same question written twice is one blank, answered once and put in both places. The title is
optional: without it the template goes by the first words of its text.plantillas_answers table after a pause in the typing (1.5 s, never more than 10 s), or at once when
the app is hidden or closed. With no connection they wait on the device and go when it comes back.
When two devices edited them apart, the later edit wins, whole: the database only replaces a row
with a later one (plantillas_save_answers), and opening a template or reconnecting compares both
copies. Every moment is UTC; only the line under the answers shows local time. Borrar empties
them for the next prompt.plantillas_templates table, with a copy on the device.me-deben:*/modelsopenai/gpt-oss-120bgroq:api-key: entered once, in whichever of the two apps comes
first, and asked for here the first time cards are generated. Only the key and what is asked for
— the topic or notes, the deck's name and the front of its cards — are ever sent to
api.groq.com..apkg file, which Elegir el
archivo .apkg opens here. A file can also be dropped on the sheet. After a few downloads AnkiWeb
asks to sign in with an account of its own, which is free.src/lib/anki/ unzips
only the collection inside, an SQLite database — never the images and audio, which can be
hundreds of megabytes — and reads it, with no library for either. What comes in is text: each
card's front and back are what Anki's templates render (fields, sections, cloze deletions, type-in
answers, hints, furigana), read as a browser would show them, without what the note type's CSS
hides, its scripts, its images or its audio. The answer drops the lines every card has alike —
credits, dashes, the label of an empty field — and the question when it repeats it. Cards whose
question is only an image or a recording, or that have no answer, are left out, and the preview
says how many, next to a few of the cards, the deck's name and its new cards a day, to change
before importing. The deck and its cards reach the database 500 cards to a request.collection.anki21b) is compressed with zstd, which browsers cannot uncompress: it
is turned down, with the way out — exporting it again with «Support older Anki versions».repaso_decks and repaso_cards tables, each card
with its place in the schedule in plain columns, and with a copy on the device so the app opens
without waiting. Coming back to the app reads them again, in case they were studied on another
device. The copy is written a second after the last change, or as the app goes to the background,
so that no answer waits for thousands of cards to be copied; and a collection too large for the
few megabytes of localStorage the whole site shares — thousands of cards, as an imported deck
brings — is not kept on the device at all, and is read from the database every time./models lists (whisper-large-v3
until another is picked; whisper-large-v3-turbo is the faster one), the language Whisper is told
the dictation is in — Spanish, English, or «Automático» for Whisper to work it out — and the chat
model that improves and edits, openai/gpt-oss-120b until another is picked. It is plain text in
and plain text out, with nothing model-specific sent, so any chat model the key can use will do.
These choices, the switch and the instructions are kept in the account.groq:api-key, and asks for one when there is
none. Only the key, the recordings and the text are ever sent to api.groq.com.dictado_texts table, one row per account, with a copy on
the device. A change is saved a moment after it is made, and until the database has it the copy on
the device says so: the next time the app opens, that copy is sent rather than read over, so a
dictation is not lost to a moment without signal. Coming back to the app reads the text again, in
case it changed on another device.itunes.apple.com/search), which needs no key: any song anyone knows, with the 30-second preview
Apple serves of it, which the post plays from Apple. From the device, an MP3 (or anything else the
browser can play). Either way the song is drawn as bars, and its moment is picked by dragging the
lit-up stretch or tapping where it should start: up to 30 seconds, which start over at the end for
as long as the post is open. A song from the device starts out on its loudest 30 seconds, more
often than not its chorus.src/lib/music/mp3.ts
cuts it out of the MP3's frames byte for byte, so it sounds exactly as the song did, with nothing
decoded or encoded. What is not an MP3 is decoded and kept as a WAV of one channel at 22 kHz,
which is what a browser can write by itself.…/leo-parti/?p=<code>): there is no
other step. Nobody comes across a post without its link, whose code is eleven random characters,
unless its author lists it in the bio. The link is copied or shared from there, from the post's
«⋯», or from the paper plane under it.…/leo-parti/?u=<username>:
the account's photo and username and a grid of its listed posts, for anybody, signed in or not,
showing each post only to whoever can open it — everybody, for one for anybody with the link;
those friends, for one for some friends. A post that is not listed is in nobody's view of the bio,
not even its friends', who open it by its link all the same. A username anywhere, on a post or by a
comment, opens its bio.leogram_profiles,
leogram_posts, leogram_likes and leogram_comments, leogram_audience (who a post for some
friends is for) and leogram_favorites, and leogram_media and leogram_avatars, which only say
where each file is. The files — photos, videos and their posters, the songs' clips, the grid's
thumbnails and the profile photos — go straight from the browser to a private bucket of the
project's Object Storage, with nothing in between. The bucket
only lets through requests signed with its key, which the browser never sees: the database keeps
it in leogram_private, a schema the Data API does not serve, and signs with it.
leogram_upload() hands over an address that takes one file of one's own post, of the type and
the exact size it was told; leogram_post(), to whoever opens the link, addresses that read the
post's files for a day at least; deleting a post, where to delete its files. It is AWS Signature
Version 4, worked out in SQL (db/migrations/0007_leogram_public.sql)./token/anonymous),
which the Data API runs as the anonymous role. That role reaches no table: all it may do is call
leogram_post(code) and leogram_profile(username), which run as their owner. The first hands
over the one post whose code it is given, never a list; the second, the posts a bio lists. Both
ask leogram_can_see(code), the one place that says who opens a post, which the policies on likes
and comments ask too (db/migrations/0012_leogram_bio.sql). Signed in, leogram_post also says
whether the post is one's own and whether one liked it, and tells its author who it is for. It
takes the Data API's anonymous role to be anonymous, which is its default.transforma_textsgroq:api-keyapi.groq.comoss.exercisedb.dev, with no key): three-second
loops of a 3D figure doing the exercise, the muscles it works in red, in the style of GymVisual's.
They load from ExerciseDB's CDN, which therefore sees which ones are shown; an exercise of a
routine can show a GIF or an MP4 of the user's own instead. The catalog is
src/lib/catalog.ts, and an exercise's id there is stored in the routines: names can be reworded,
ids never change.rutina_routines, one row each with its days and exercises as JSON, and
workouts in rutina_sessions, one row per day trained with every set done. A session is saved
whole after every set. A gym is as likely as not to have no signal: a session the database did
not get stays on the device as pending, and goes again every time the app opens — and with every
set after it — until it does. Both tables have a copy on the device, so the app opens without
waiting.caminadora_programs table, one row per program with
its segments in a jsonb column, and a copy on the device so the app opens without waiting.
Coming back to the app reads them again, in case they changed on another device.