@sempods/apps 0.0.0-stage → 0.7.0-dev.1
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/CHANGELOG.md +205 -0
- package/LICENSE +19 -0
- package/README.md +50 -2
- package/bin/sempods-apps.mjs +36 -0
- package/instructions/app-workflow.md +210 -0
- package/instructions/publish.md +148 -0
- package/instructions/setup.md +112 -0
- package/package.json +59 -3
- package/scripts/build-site.mjs +181 -0
- package/scripts/check.mjs +260 -0
- package/scripts/configure-site.mjs +118 -0
- package/scripts/create.mjs +83 -0
- package/scripts/dev.mjs +19 -0
- package/scripts/lib/apps.mjs +164 -0
- package/scripts/lib/files.mjs +57 -0
- package/scripts/lib/generate.mjs +175 -0
- package/scripts/lib/html.mjs +3 -0
- package/scripts/lib/json.mjs +42 -0
- package/scripts/lib/merge.mjs +159 -0
- package/scripts/lib/overview.mjs +105 -0
- package/scripts/lib/paths.mjs +33 -0
- package/scripts/lib/pnpm.mjs +30 -0
- package/scripts/lib/sdk.mjs +45 -0
- package/scripts/lib/shared.mjs +221 -0
- package/scripts/migrate.mjs +550 -0
- package/scripts/new-app.mjs +166 -0
- package/scripts/regenerate.mjs +41 -0
- package/scripts/sdk-update.mjs +220 -0
- package/scripts/update.mjs +152 -0
- package/shared/files/0221b70340152ba3cc615342254603b29b89c4d7d944d7b3b3e647873bdcefa0 +11 -0
- package/shared/files/1c29eed0a0297c742ca5814de9081ff8efce3b2da5d9e1bdf90a403873da4aa9 +61 -0
- package/shared/files/31ede0d1a035d8531100ebfd751510c772ab0871309a0b9752a77fb96187eb53 +25 -0
- package/shared/files/393b09ecd18889d9e1fdf2a6df12d1f6c193fc7fcd89ba808ad94df521bad8db +41 -0
- package/shared/files/4660ca1887261ae658c7f587190e2b71ac91d15b1b6c4cf04dec823fdd0da654 +22 -0
- package/shared/files/528d7544990228d6d66fa62423775338fe1716326fc68371fb2665aee86e39b0 +6 -0
- package/shared/files/676e6bf33b44044fb4c62c9fe5441729bd47f2d5077ff904e57a4a1b658859ac +38 -0
- package/shared/files/8b7567319e18694aeca8722107281a760e3100239c6d0f548acb71e1806fa172 +28 -0
- package/shared/files/9945f9bd573e5af8aad2e65cc8b4284fbb1d7e8b96c4efdd42eeb49132fb4327 +4 -0
- package/shared/files/b911698dc3cf3227d34d0ad6727f1c4408791203c9665de99ab959331a1fb07d +1 -0
- package/shared/files/c2a4b026a87f6c8e7473e7fc456c6020b63c7e675087f6b58013ed5163c2f6ee +23 -0
- package/shared/files/d3ca3a13c6524a2ee9382977ddc0539790813225f366306024a58c957a4716ee +11 -0
- package/shared/files/e13474647ebaf26d042d76f4917203d209a2b14c0d2af8d12139ea328530823e +78 -0
- package/shared/files/e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855 +0 -0
- package/shared/files/f2fb601d5ebc4ca72274ac9d3fcf7b7a96e27958bb92cd065a9165b501ee94d7 +57 -0
- package/shared/files/f9e2544290ee8418484e47194444ee58c2ea675682871ae8a8727d1edb17978e +5 -0
- package/shared/files/ff9f774d9732fdc7d704ef37ddc40ef507a1bab7ee3c1db5b1a6a8f26e6e4b37 +35 -0
- package/shared/snapshot.json +25 -0
- package/skeleton/app/.oxlintrc.json +14 -0
- package/skeleton/app/NOTES.md +19 -0
- package/skeleton/app/index.html +16 -0
- package/skeleton/app/package.json +28 -0
- package/skeleton/app/public/favicon.svg +1 -0
- package/skeleton/app/public/icon-192.png +0 -0
- package/skeleton/app/public/icon-512.png +0 -0
- package/skeleton/app/src/App.tsx +73 -0
- package/skeleton/app/src/NewVersionNotice.tsx +63 -0
- package/skeleton/app/src/index.css +37 -0
- package/skeleton/app/src/main.tsx +18 -0
- package/skeleton/app/src/pwa.ts +18 -0
- package/skeleton/app/src/start.test.ts +15 -0
- package/skeleton/app/src/start.ts +9 -0
- package/skeleton/app/tsconfig.app.json +27 -0
- package/skeleton/app/tsconfig.json +8 -0
- package/skeleton/app/tsconfig.node.json +23 -0
- package/skeleton/app/tsconfig.test.json +10 -0
- package/skeleton/app/vite.config.ts +13 -0
- package/skills/app-workflow/SKILL.md +22 -0
- package/skills/update/SKILL.md +69 -0
- package/update-policy.json +35 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Working in this apps repository
|
|
2
|
+
|
|
3
|
+
Scope: the whole repository. More specific app instructions can refine app work;
|
|
4
|
+
the owner's requests and recorded choices determine the task.
|
|
5
|
+
|
|
6
|
+
The owner describes what they need; you build small, usable steps for them to
|
|
7
|
+
try. Do not assume developer knowledge. Explain what to try and any choice in
|
|
8
|
+
everyday terms, in the owner's language. Make routine technical choices from
|
|
9
|
+
the workflow and installed SDK reference, and record them in app notes. Ask
|
|
10
|
+
only for the decisions that setup and the workflow leave to the owner, and use
|
|
11
|
+
answers already given.
|
|
12
|
+
|
|
13
|
+
The instructions you follow ship with the installed tooling package
|
|
14
|
+
`@sempods/apps` and the SDK. If `node_modules` is missing, follow the
|
|
15
|
+
bootstrap steps in [INIT.md](INIT.md) first.
|
|
16
|
+
|
|
17
|
+
1. On first setup, read [INIT.md](INIT.md). On later sessions, read its setup
|
|
18
|
+
record and the target app's `apps/<id>/NOTES.md` first.
|
|
19
|
+
2. Follow the app workflow,
|
|
20
|
+
`node_modules/@sempods/apps/instructions/app-workflow.md`, then the SDK's AI
|
|
21
|
+
entry, `node_modules/@sempods/app-sdk/docs/ai-app-builder.md`, and the
|
|
22
|
+
guides relevant to the change. They match the installed versions; use those
|
|
23
|
+
versions' API.
|
|
24
|
+
3. Keep app-specific decisions in its notes and repository-wide choices in the
|
|
25
|
+
owner section below. Do not place credentials or private Pod data there.
|
|
26
|
+
|
|
27
|
+
The app-workflow skill in `.claude/skills/` and `.agents/skills/` routes to the
|
|
28
|
+
same workflow; skill discovery is optional. To update the tooling, follow the
|
|
29
|
+
update skill there, `node_modules/@sempods/apps/skills/update/SKILL.md`.
|
|
30
|
+
[User steps](docs/start.md) describe the same journey for the owner.
|
|
31
|
+
|
|
32
|
+
## Owner instructions
|
|
33
|
+
|
|
34
|
+
Updates must preserve the delimited section below verbatim. Only change it to
|
|
35
|
+
record this owner's instructions or decisions.
|
|
36
|
+
|
|
37
|
+
<!-- BEGIN OWNER INSTRUCTIONS -->
|
|
38
|
+
|
|
39
|
+
No instance-specific instructions recorded yet.
|
|
40
|
+
|
|
41
|
+
<!-- END OWNER INSTRUCTIONS -->
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
packages:
|
|
2
|
+
- 'apps/*'
|
|
3
|
+
|
|
4
|
+
# Added dependencies get exact versions; installs require the Node version
|
|
5
|
+
# from engines.
|
|
6
|
+
saveExact: true
|
|
7
|
+
engineStrict: true
|
|
8
|
+
|
|
9
|
+
# Scripts fail on dependencies out of sync with the manifests instead of
|
|
10
|
+
# installing on their own, so check never rewrites pnpm-lock.yaml.
|
|
11
|
+
verifyDepsBeforeRun: error
|
|
12
|
+
|
|
13
|
+
# Dependencies run no install scripts: pnpm fails an install that would need
|
|
14
|
+
# one (strictDepBuilds). Approve a package deliberately under allowBuilds.
|
|
15
|
+
|
|
16
|
+
# Local installs have no minimum release age: pnpm would otherwise install a
|
|
17
|
+
# dependency younger than a day anyway when nothing older fits, add it to this
|
|
18
|
+
# file and stop the next `pnpm run` (verifyDepsBeforeRun). Dependabot applies
|
|
19
|
+
# a cooldown to regular version updates (.github/dependabot.yml). That is no
|
|
20
|
+
# universal age guarantee: local dependency changes, security updates and
|
|
21
|
+
# pnpm fallback paths can admit younger releases.
|
|
22
|
+
minimumReleaseAge: 0
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# My sempods apps
|
|
2
|
+
|
|
3
|
+
Describe a shopping list, a small tracker or another idea to your coding
|
|
4
|
+
assistant. Try the first version, then ask for the next change. Your
|
|
5
|
+
[sempods](https://www.sempods.org/) Pod keeps the data; the app gives it a
|
|
6
|
+
screen.
|
|
7
|
+
|
|
8
|
+
You need a coding assistant that can work with local files, Node.js in the
|
|
9
|
+
version `.node-version` names, pnpm, and a Pod account for trying real saves.
|
|
10
|
+
Your assistant helps with the local setup. A hosting account is needed only to
|
|
11
|
+
publish.
|
|
12
|
+
|
|
13
|
+
> **Preview.** This starter is new and still changing. Today you can build
|
|
14
|
+
> apps, try them locally against your Pod, update the SDK and the tooling, and
|
|
15
|
+
> publish all apps together on one site.
|
|
16
|
+
|
|
17
|
+
## Start with your idea
|
|
18
|
+
|
|
19
|
+
Open this repository in your coding assistant and paste:
|
|
20
|
+
|
|
21
|
+
> Read AGENTS.md and INIT.md. Help me build a shopping list in German. Start
|
|
22
|
+
> with adding an item and marking it as bought. Show me how to try it on my Pod.
|
|
23
|
+
|
|
24
|
+
[Start, change, try, publish](docs/start.md) explains the next steps. Sign in
|
|
25
|
+
only in your browser; never paste passwords or tokens into the chat.
|
|
26
|
+
|
|
27
|
+
Apps include PWA configuration by default, for later installation from your
|
|
28
|
+
HTTPS site. That does not make data editing work offline.
|
|
29
|
+
|
|
30
|
+
For assistants: [AGENTS.md](AGENTS.md) and [INIT.md](INIT.md).
|
|
31
|
+
|
|
32
|
+
## Licence
|
|
33
|
+
|
|
34
|
+
Your apps and this repository are yours; add the licence you choose. The
|
|
35
|
+
tooling package `@sempods/apps` is MIT-0. The SDK packages, including the
|
|
36
|
+
documentation and examples they ship, keep their own licences: Apache-2.0 for
|
|
37
|
+
code and CC BY 4.0 for documentation. Code adapted from SDK examples keeps its
|
|
38
|
+
Apache-2.0 notice.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: Check
|
|
2
|
+
# Checks the apps as they are: manifest, generated configuration, the shared
|
|
3
|
+
# SDK version, and lint, build and tests of each app, also on its own.
|
|
4
|
+
on:
|
|
5
|
+
pull_request:
|
|
6
|
+
push:
|
|
7
|
+
branches: [main]
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
jobs:
|
|
12
|
+
check:
|
|
13
|
+
# A ruleset can require this check by name; keep it stable.
|
|
14
|
+
name: Apps
|
|
15
|
+
runs-on: ubuntu-latest
|
|
16
|
+
timeout-minutes: 20
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
19
|
+
with:
|
|
20
|
+
persist-credentials: false
|
|
21
|
+
# Installs the pnpm version that packageManager in package.json pins.
|
|
22
|
+
- uses: pnpm/action-setup@ea17c68df8912ef543352723c149a84f56e3d413 # v6.1.0
|
|
23
|
+
- uses: actions/setup-node@949feb2413d6458794dcd2491c4babbbce0c15c1 # v7.1.0
|
|
24
|
+
with:
|
|
25
|
+
node-version-file: .node-version
|
|
26
|
+
cache: pnpm
|
|
27
|
+
- run: pnpm install --frozen-lockfile
|
|
28
|
+
- run: pnpm run check --standalone all
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
24.15.0
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
node_modules/
|
|
2
|
+
dist/
|
|
3
|
+
site-dist/
|
|
4
|
+
.netlify/
|
|
5
|
+
*.tsbuildinfo
|
|
6
|
+
.env
|
|
7
|
+
.env.*
|
|
8
|
+
!.env.example
|
|
9
|
+
.DS_Store
|
|
10
|
+
dev-dist/
|
|
11
|
+
# Personal assistant instructions stay local.
|
|
12
|
+
CLAUDE.local.md
|
|
13
|
+
AGENTS.override.md
|
|
14
|
+
|
|
15
|
+
# Editor settings stay local.
|
|
16
|
+
.idea/
|
|
17
|
+
.vscode/
|
|
18
|
+
|
|
19
|
+
# Local SDK update checkpoint; removed only after install and checks succeed.
|
|
20
|
+
/.sdk-update-pending
|
|
21
|
+
# Open starter migration: manual cases, their sources and suggestions;
|
|
22
|
+
# removed once the migration is complete.
|
|
23
|
+
/.sempods-migration/
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: update
|
|
3
|
+
description:
|
|
4
|
+
Update the tooling of this sempods apps repository: install a newer
|
|
5
|
+
@sempods/apps, migrate the shared files, review and open a pull request.
|
|
6
|
+
Not for SDK-only updates.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
Read the canonical update skill that the installed tooling package ships,
|
|
10
|
+
`node_modules/@sempods/apps/skills/update/SKILL.md`. If `node_modules` is
|
|
11
|
+
missing, follow the bootstrap steps in `INIT.md` first.
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Version updates. Security updates are enabled in the repository settings.
|
|
2
|
+
version: 2
|
|
3
|
+
|
|
4
|
+
updates:
|
|
5
|
+
# The pinned `uses:` SHAs in the workflows are supply-chain dependencies too.
|
|
6
|
+
- package-ecosystem: 'github-actions'
|
|
7
|
+
directory: '/'
|
|
8
|
+
schedule:
|
|
9
|
+
interval: 'weekly'
|
|
10
|
+
# Reviewed releases of shared sempods actions need no waiting period;
|
|
11
|
+
# third-party actions keep Dependabot's default three-day cooldown.
|
|
12
|
+
cooldown:
|
|
13
|
+
exclude:
|
|
14
|
+
- 'sempods/.github/*'
|
|
15
|
+
groups:
|
|
16
|
+
github-actions:
|
|
17
|
+
patterns:
|
|
18
|
+
- '*'
|
|
19
|
+
update-types:
|
|
20
|
+
- 'minor'
|
|
21
|
+
- 'patch'
|
|
22
|
+
|
|
23
|
+
# Dependabot's npm ecosystem updates pnpm-lock.yaml too, and every
|
|
24
|
+
# package.json of the pnpm workspace: the root and each app.
|
|
25
|
+
- package-ecosystem: 'npm'
|
|
26
|
+
directory: '/'
|
|
27
|
+
schedule:
|
|
28
|
+
interval: 'weekly'
|
|
29
|
+
versioning-strategy: 'increase'
|
|
30
|
+
# Regular version updates wait three days; Dependabot also attempts an age
|
|
31
|
+
# gate for newly resolved transitive dependencies. Security updates skip
|
|
32
|
+
# it. sempods releases are reviewed upstream and need no waiting period.
|
|
33
|
+
cooldown:
|
|
34
|
+
default-days: 3
|
|
35
|
+
exclude:
|
|
36
|
+
- '@sempods/*'
|
|
37
|
+
# Ignore rules with update-types stop only version updates; security
|
|
38
|
+
# updates for these packages still arrive.
|
|
39
|
+
ignore:
|
|
40
|
+
# BEGIN OWNER SEMPODS UPDATES
|
|
41
|
+
# No version PRs for the SDK and the tooling. Remove the entry between
|
|
42
|
+
# these markers to receive them; updates keep this section as it is.
|
|
43
|
+
- dependency-name: '@sempods/*'
|
|
44
|
+
update-types:
|
|
45
|
+
- 'version-update:semver-major'
|
|
46
|
+
- 'version-update:semver-minor'
|
|
47
|
+
- 'version-update:semver-patch'
|
|
48
|
+
# END OWNER SEMPODS UPDATES
|
|
49
|
+
# Match the Node 24 runtime baseline; review runtime and typings together.
|
|
50
|
+
- dependency-name: '@types/node'
|
|
51
|
+
update-types:
|
|
52
|
+
- 'version-update:semver-major'
|
|
53
|
+
# A dependency joins the first group it matches. Both SDK packages move
|
|
54
|
+
# together in one PR; the tooling has its own, since it brings a starter
|
|
55
|
+
# migration (update skill). Security updates move only affected packages:
|
|
56
|
+
# their SDK group joins advisories for both, and a PR that moves one is
|
|
57
|
+
# completed with sdk-update (Update the SDK in the app workflow).
|
|
58
|
+
groups:
|
|
59
|
+
sempods-sdk:
|
|
60
|
+
patterns:
|
|
61
|
+
- '@sempods/app-sdk'
|
|
62
|
+
- '@sempods/client-sdk'
|
|
63
|
+
sempods-sdk-security:
|
|
64
|
+
applies-to: 'security-updates'
|
|
65
|
+
patterns:
|
|
66
|
+
- '@sempods/app-sdk'
|
|
67
|
+
- '@sempods/client-sdk'
|
|
68
|
+
sempods-apps:
|
|
69
|
+
patterns:
|
|
70
|
+
- '@sempods/apps'
|
|
71
|
+
npm:
|
|
72
|
+
patterns:
|
|
73
|
+
- '*'
|
|
74
|
+
exclude-patterns:
|
|
75
|
+
- '@sempods/*'
|
|
76
|
+
update-types:
|
|
77
|
+
- 'minor'
|
|
78
|
+
- 'patch'
|
|
File without changes
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Set up this repository
|
|
2
|
+
|
|
3
|
+
This is the assistant's starting point for setup. Users can start with
|
|
4
|
+
[the short guide](docs/start.md). Follow [AGENTS.md](AGENTS.md) and preserve the
|
|
5
|
+
setup record below on later runs and updates.
|
|
6
|
+
|
|
7
|
+
## Bootstrap
|
|
8
|
+
|
|
9
|
+
The setup procedure ships with the tooling package, so install the
|
|
10
|
+
dependencies first. Check `node --version` against `.node-version`. If it
|
|
11
|
+
differs, switch with the owner's version manager and name that version
|
|
12
|
+
explicitly; not every manager reads `.node-version` by default, and with mise
|
|
13
|
+
prefix each command with `mise exec node@<version> --`. Do not write a
|
|
14
|
+
version-manager configuration into the repository: `.node-version` stays the
|
|
15
|
+
only source. Otherwise ask the owner to install that version; do not work
|
|
16
|
+
around it. `pnpm-workspace.yaml` sets `engineStrict`, so `pnpm install` stops
|
|
17
|
+
on an older Node.
|
|
18
|
+
|
|
19
|
+
Then check `pnpm --version`. If pnpm is missing, Node 24 brings Corepack:
|
|
20
|
+
`corepack enable pnpm`. Without Corepack (Node 25 and later), or if it cannot
|
|
21
|
+
write to Node's directory, install pnpm globally with npm after asking the
|
|
22
|
+
owner. pnpm switches to the version that `packageManager` in `package.json`
|
|
23
|
+
pins. From the repository root:
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
pnpm install
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
A repository that has committed `pnpm-lock.yaml` installs with
|
|
30
|
+
`pnpm install --frozen-lockfile` instead. Commit the lockfile the first install
|
|
31
|
+
writes.
|
|
32
|
+
|
|
33
|
+
## Setup procedure
|
|
34
|
+
|
|
35
|
+
Follow `node_modules/@sempods/apps/instructions/setup.md`. It covers the first
|
|
36
|
+
app, optional repository preferences and when setup is done. Record progress
|
|
37
|
+
only in the setup record below.
|
|
38
|
+
|
|
39
|
+
## Setup record
|
|
40
|
+
|
|
41
|
+
The assistant updates only this record as setup progresses; updates preserve
|
|
42
|
+
it. Keep it free of credentials and private data.
|
|
43
|
+
|
|
44
|
+
<!-- BEGIN INSTANCE SETUP RECORD -->
|
|
45
|
+
|
|
46
|
+
- Status: not started
|
|
47
|
+
- Created from (`.sempods-baseline.json`): not recorded
|
|
48
|
+
- SDK version at setup (`@sempods/app-sdk` in the root `package.json`): not recorded
|
|
49
|
+
- Owner and review identity: not recorded
|
|
50
|
+
- UI language: not selected
|
|
51
|
+
- First app ID and idea: not selected
|
|
52
|
+
- Test Pod/context: not selected (optional; synthetic data only)
|
|
53
|
+
- sempods version PRs (Dependabot): not chosen
|
|
54
|
+
- Local URL and check result: not checked
|
|
55
|
+
- Pod exercise and remaining gaps: not tested
|
|
56
|
+
|
|
57
|
+
<!-- END INSTANCE SETUP RECORD -->
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
name: CodeQL
|
|
2
|
+
on:
|
|
3
|
+
pull_request:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
schedule:
|
|
7
|
+
- cron: '41 6 * * 2'
|
|
8
|
+
workflow_dispatch:
|
|
9
|
+
permissions:
|
|
10
|
+
contents: read
|
|
11
|
+
jobs:
|
|
12
|
+
analyze:
|
|
13
|
+
name: Analyze (${{ matrix.language }})
|
|
14
|
+
runs-on: ubuntu-latest
|
|
15
|
+
timeout-minutes: 20
|
|
16
|
+
permissions:
|
|
17
|
+
contents: read
|
|
18
|
+
security-events: write
|
|
19
|
+
strategy:
|
|
20
|
+
fail-fast: false
|
|
21
|
+
matrix:
|
|
22
|
+
language: [actions, javascript-typescript]
|
|
23
|
+
steps:
|
|
24
|
+
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
|
25
|
+
with:
|
|
26
|
+
persist-credentials: false
|
|
27
|
+
- uses: github/codeql-action/init@24c54180a607b1449ed407dd24f251e4e9147c8d # v4
|
|
28
|
+
with:
|
|
29
|
+
languages: ${{ matrix.language }}
|
|
30
|
+
build-mode: none
|
|
31
|
+
config-file: ./.github/codeql/codeql-config.yml
|
|
32
|
+
queries: security-extended
|
|
33
|
+
- uses: github/codeql-action/analyze@24c54180a607b1449ed407dd24f251e4e9147c8d # v4
|
|
34
|
+
with:
|
|
35
|
+
category: /language:${{ matrix.language }}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"format": 1,
|
|
3
|
+
"revision": "71735f2f98610875bee974bb9b42651505d4b395dcdb02eb8ac8ff9ed48c6e2c",
|
|
4
|
+
"files": {
|
|
5
|
+
".agents/skills/app-workflow/SKILL.md": "0221b70340152ba3cc615342254603b29b89c4d7d944d7b3b3e647873bdcefa0",
|
|
6
|
+
".agents/skills/update/SKILL.md": "d3ca3a13c6524a2ee9382977ddc0539790813225f366306024a58c957a4716ee",
|
|
7
|
+
".claude/skills/app-workflow/SKILL.md": "0221b70340152ba3cc615342254603b29b89c4d7d944d7b3b3e647873bdcefa0",
|
|
8
|
+
".claude/skills/update/SKILL.md": "d3ca3a13c6524a2ee9382977ddc0539790813225f366306024a58c957a4716ee",
|
|
9
|
+
".github/codeql/codeql-config.yml": "528d7544990228d6d66fa62423775338fe1716326fc68371fb2665aee86e39b0",
|
|
10
|
+
".github/dependabot.yml": "e13474647ebaf26d042d76f4917203d209a2b14c0d2af8d12139ea328530823e",
|
|
11
|
+
".github/workflows/check.yml": "8b7567319e18694aeca8722107281a760e3100239c6d0f548acb71e1806fa172",
|
|
12
|
+
".github/workflows/codeql.yml": "ff9f774d9732fdc7d704ef37ddc40ef507a1bab7ee3c1db5b1a6a8f26e6e4b37",
|
|
13
|
+
".gitignore": "c2a4b026a87f6c8e7473e7fc456c6020b63c7e675087f6b58013ed5163c2f6ee",
|
|
14
|
+
".node-version": "b911698dc3cf3227d34d0ad6727f1c4408791203c9665de99ab959331a1fb07d",
|
|
15
|
+
"AGENTS.md": "393b09ecd18889d9e1fdf2a6df12d1f6c193fc7fcd89ba808ad94df521bad8db",
|
|
16
|
+
"CLAUDE.md": "f9e2544290ee8418484e47194444ee58c2ea675682871ae8a8727d1edb17978e",
|
|
17
|
+
"INIT.md": "f2fb601d5ebc4ca72274ac9d3fcf7b7a96e27958bb92cd065a9165b501ee94d7",
|
|
18
|
+
"README.md": "676e6bf33b44044fb4c62c9fe5441729bd47f2d5077ff904e57a4a1b658859ac",
|
|
19
|
+
"apps.json": "9945f9bd573e5af8aad2e65cc8b4284fbb1d7e8b96c4efdd42eeb49132fb4327",
|
|
20
|
+
"apps/.gitkeep": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
|
|
21
|
+
"docs/start.md": "1c29eed0a0297c742ca5814de9081ff8efce3b2da5d9e1bdf90a403873da4aa9",
|
|
22
|
+
"package.json": "31ede0d1a035d8531100ebfd751510c772ab0871309a0b9752a77fb96187eb53",
|
|
23
|
+
"pnpm-workspace.yaml": "4660ca1887261ae658c7f587190e2b71ac91d15b1b6c4cf04dec823fdd0da654"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "./node_modules/oxlint/configuration_schema.json",
|
|
3
|
+
"plugins": ["react", "typescript", "oxc"],
|
|
4
|
+
"rules": {
|
|
5
|
+
"react/rules-of-hooks": "error",
|
|
6
|
+
"react/only-export-components": [
|
|
7
|
+
"warn",
|
|
8
|
+
{
|
|
9
|
+
"allowConstantExport": true
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
"ignorePatterns": ["node_modules", "dist", "dev-dist"]
|
|
14
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# __APP_TITLE__: development notes
|
|
2
|
+
|
|
3
|
+
Your assistant keeps these notes up to date so the next session can continue.
|
|
4
|
+
|
|
5
|
+
## Idea
|
|
6
|
+
|
|
7
|
+
What the app is for and its first useful interaction.
|
|
8
|
+
|
|
9
|
+
## Vocabulary
|
|
10
|
+
|
|
11
|
+
Types and properties (full IRIs) the app reads and writes.
|
|
12
|
+
|
|
13
|
+
## Decisions
|
|
14
|
+
|
|
15
|
+
Choices the owner made, with the date.
|
|
16
|
+
|
|
17
|
+
## Tested
|
|
18
|
+
|
|
19
|
+
What was tried, against which Pod and context, and what is still untested.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="__APP_LANGUAGE__">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8" />
|
|
5
|
+
<link rel="icon" type="image/svg+xml" href="/favicon.svg" />
|
|
6
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
|
+
<meta name="theme-color" content="#176354" />
|
|
8
|
+
<!-- The callback page must not send its code and state in Referer. -->
|
|
9
|
+
<meta name="referrer" content="strict-origin" />
|
|
10
|
+
<title>__APP_TITLE__</title>
|
|
11
|
+
</head>
|
|
12
|
+
<body>
|
|
13
|
+
<div id="root"></div>
|
|
14
|
+
<script type="module" src="/src/main.tsx"></script>
|
|
15
|
+
</body>
|
|
16
|
+
</html>
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "__APP_ID__",
|
|
3
|
+
"private": true,
|
|
4
|
+
"version": "0.0.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"dev": "vite",
|
|
8
|
+
"build": "tsc -b && vite build",
|
|
9
|
+
"lint": "oxlint",
|
|
10
|
+
"test": "vitest run --passWithNoTests",
|
|
11
|
+
"preview": "vite preview"
|
|
12
|
+
},
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"react": "19.3.0",
|
|
15
|
+
"react-dom": "19.3.0"
|
|
16
|
+
},
|
|
17
|
+
"devDependencies": {
|
|
18
|
+
"@types/node": "24.19.1",
|
|
19
|
+
"@types/react": "19.3.0",
|
|
20
|
+
"@types/react-dom": "19.3.0",
|
|
21
|
+
"@vitejs/plugin-react": "6.1.2",
|
|
22
|
+
"oxlint": "1.87.0",
|
|
23
|
+
"typescript": "7.0.2",
|
|
24
|
+
"vite": "8.3.3",
|
|
25
|
+
"vite-plugin-pwa": "2.0.0",
|
|
26
|
+
"vitest": "5.0.3"
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" rx="6" fill="#176354"/></svg>
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { useRef, useState } from 'react';
|
|
2
|
+
import { createBrowserRuntime } from '@sempods/app-sdk';
|
|
3
|
+
import {
|
|
4
|
+
AppAccess,
|
|
5
|
+
SempodsProvider,
|
|
6
|
+
TargetScreen,
|
|
7
|
+
useAppState,
|
|
8
|
+
useSdkLocale,
|
|
9
|
+
} from '@sempods/app-sdk/react';
|
|
10
|
+
import { NewVersionNotice } from './NewVersionNotice.tsx';
|
|
11
|
+
import { app, runtimeOptions } from './sempods.generated.ts';
|
|
12
|
+
import { startMessage } from './start.ts';
|
|
13
|
+
|
|
14
|
+
// One runtime for the app's lifetime, created outside rendering.
|
|
15
|
+
const runtime = createBrowserRuntime(runtimeOptions);
|
|
16
|
+
|
|
17
|
+
// Vite hot replacement ends this module's runtime lifetime.
|
|
18
|
+
if (import.meta.hot) import.meta.hot.dispose(() => runtime.dispose());
|
|
19
|
+
|
|
20
|
+
export default function App({
|
|
21
|
+
registration,
|
|
22
|
+
}: {
|
|
23
|
+
readonly registration: Promise<ServiceWorkerRegistration | undefined>;
|
|
24
|
+
}) {
|
|
25
|
+
return (
|
|
26
|
+
<SempodsProvider runtime={runtime} language={app.language}>
|
|
27
|
+
<NewVersionNotice registration={registration} />
|
|
28
|
+
<Frame />
|
|
29
|
+
</SempodsProvider>
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// The app owns its layout. AppAccess shows sign-in and recovery beside the
|
|
34
|
+
// content and hides once a context is readable; the button reopens it.
|
|
35
|
+
function Frame() {
|
|
36
|
+
const [open, setOpen] = useState(false);
|
|
37
|
+
const focusTarget = useRef<HTMLButtonElement>(null);
|
|
38
|
+
const { connections } = useAppState();
|
|
39
|
+
const { messages } = useSdkLocale();
|
|
40
|
+
const header = connections.length > 0;
|
|
41
|
+
return (
|
|
42
|
+
<main>
|
|
43
|
+
{header && (
|
|
44
|
+
<header>
|
|
45
|
+
<h1>{app.title}</h1>
|
|
46
|
+
<button
|
|
47
|
+
ref={focusTarget}
|
|
48
|
+
aria-expanded={open}
|
|
49
|
+
onClick={() => setOpen(!open)}
|
|
50
|
+
>
|
|
51
|
+
{messages.controls.dataAccess}
|
|
52
|
+
</button>
|
|
53
|
+
</header>
|
|
54
|
+
)}
|
|
55
|
+
{/* Below the header's h1, AppAccess uses h2. */}
|
|
56
|
+
<AppAccess
|
|
57
|
+
appName={app.title}
|
|
58
|
+
headingLevel={header ? 2 : 1}
|
|
59
|
+
open={open}
|
|
60
|
+
focusTarget={focusTarget}
|
|
61
|
+
/>
|
|
62
|
+
<TargetScreen>
|
|
63
|
+
<Start />
|
|
64
|
+
</TargetScreen>
|
|
65
|
+
</main>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Replace this screen with your app. It renders once a Pod context is chosen.
|
|
70
|
+
function Start() {
|
|
71
|
+
const { language } = useSdkLocale();
|
|
72
|
+
return <p>{startMessage(language)}</p>;
|
|
73
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
// Adapted from the sempods TypeScript SDK v0.2.0 recipe
|
|
2
|
+
// examples/todo/pwa/register.tsx, licensed under Apache-2.0; see
|
|
3
|
+
// the LICENSE and NOTICE of the @sempods/app-sdk package. Changes: this header; the
|
|
4
|
+
// registration function moved to pwa.ts.
|
|
5
|
+
import { useEffect, useState } from 'react';
|
|
6
|
+
import { useSdkLocale } from '@sempods/app-sdk/react';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Render beneath the app's actual locale provider so notices follow its language.
|
|
10
|
+
* Keep it outside target/access gates: updates also matter while signed out.
|
|
11
|
+
* Announces a downloaded version. It applies once every window of the app is
|
|
12
|
+
* closed, so an update never reloads a page over unsaved drafts or unconfirmed
|
|
13
|
+
* writes. App-owned recipe, not an SDK export.
|
|
14
|
+
*/
|
|
15
|
+
export function NewVersionNotice({
|
|
16
|
+
registration,
|
|
17
|
+
}: {
|
|
18
|
+
readonly registration: Promise<ServiceWorkerRegistration | undefined>;
|
|
19
|
+
}) {
|
|
20
|
+
const [ready, setReady] = useState(false);
|
|
21
|
+
const { language } = useSdkLocale();
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
let active = true;
|
|
24
|
+
let stop = () => {};
|
|
25
|
+
void registration.then((r) => {
|
|
26
|
+
if (!r || !active) return;
|
|
27
|
+
// Only an update of a controlled page is news; a first install is not.
|
|
28
|
+
const check = () => {
|
|
29
|
+
if (active && r.waiting && navigator.serviceWorker.controller)
|
|
30
|
+
setReady(true);
|
|
31
|
+
};
|
|
32
|
+
// Follow a worker that was already installing before this subscription
|
|
33
|
+
// (updatefound fired earlier) as well as every later one.
|
|
34
|
+
const watched = new Set<ServiceWorker>();
|
|
35
|
+
const watch = (worker: ServiceWorker | null) => {
|
|
36
|
+
if (!worker || watched.has(worker)) return;
|
|
37
|
+
watched.add(worker);
|
|
38
|
+
worker.addEventListener('statechange', check);
|
|
39
|
+
};
|
|
40
|
+
const found = () => watch(r.installing);
|
|
41
|
+
r.addEventListener('updatefound', found);
|
|
42
|
+
watch(r.installing);
|
|
43
|
+
stop = () => {
|
|
44
|
+
r.removeEventListener('updatefound', found);
|
|
45
|
+
for (const worker of watched)
|
|
46
|
+
worker.removeEventListener('statechange', check);
|
|
47
|
+
};
|
|
48
|
+
check();
|
|
49
|
+
});
|
|
50
|
+
return () => {
|
|
51
|
+
active = false;
|
|
52
|
+
stop();
|
|
53
|
+
};
|
|
54
|
+
}, [registration]);
|
|
55
|
+
if (!ready) return null;
|
|
56
|
+
return (
|
|
57
|
+
<p role="status">
|
|
58
|
+
{language === 'de'
|
|
59
|
+
? 'Eine neue Version ist bereit. Sie wird verwendet, sobald alle Fenster dieser App geschlossen sind.'
|
|
60
|
+
: 'A new version is ready. It is used once every window of this app has been closed.'}
|
|
61
|
+
</p>
|
|
62
|
+
);
|
|
63
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
/* Follow the system's light or dark mode; the SDK components follow the page.
|
|
3
|
+
The colours match the SDK defaults, so AppAccess sits seamlessly on the page. */
|
|
4
|
+
color-scheme: light dark;
|
|
5
|
+
font-family: system-ui, sans-serif;
|
|
6
|
+
color: light-dark(#202923, #e8eee9);
|
|
7
|
+
background: light-dark(#fff, #191b1a);
|
|
8
|
+
}
|
|
9
|
+
body {
|
|
10
|
+
margin: 0;
|
|
11
|
+
}
|
|
12
|
+
main {
|
|
13
|
+
max-width: 760px;
|
|
14
|
+
margin: auto;
|
|
15
|
+
padding: 16px;
|
|
16
|
+
}
|
|
17
|
+
header {
|
|
18
|
+
display: flex;
|
|
19
|
+
flex-wrap: wrap;
|
|
20
|
+
align-items: center;
|
|
21
|
+
justify-content: space-between;
|
|
22
|
+
gap: 0.5rem;
|
|
23
|
+
}
|
|
24
|
+
header h1 {
|
|
25
|
+
margin: 0;
|
|
26
|
+
font-size: 1.4rem;
|
|
27
|
+
}
|
|
28
|
+
button,
|
|
29
|
+
input,
|
|
30
|
+
select {
|
|
31
|
+
font: inherit;
|
|
32
|
+
margin: 0.25rem;
|
|
33
|
+
padding: 0.4rem;
|
|
34
|
+
}
|
|
35
|
+
input[type='checkbox'] {
|
|
36
|
+
accent-color: light-dark(#226044, #8acda6);
|
|
37
|
+
}
|