Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 41 additions & 14 deletions .github/workflows/deploy-site.yml
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ jobs:
environment:
name: ${{ inputs.environment }}
url: ${{ steps.deploy.outputs.url }}
permissions:
contents: read
id-token: write # assume the environment's AWS role
outputs:
worker: ${{ steps.worker.outputs.deploy }}
steps:
Expand Down Expand Up @@ -86,18 +89,31 @@ jobs:
- name: Test site
run: bun run --cwd apps/web test

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@e1253824e5c10ff9df46874f81ed3ec929e19cfd # v6.3.0
with:
role-to-assume: arn:aws:iam::604439173067:role/github/textjs-${{ inputs.environment }}
role-session-name: textjs-secrets-${{ github.run_id }}
aws-region: us-east-1
- name: Fetch the deploy secrets
uses: aws-actions/aws-secretsmanager-get-secrets@2297f9a879480a9e3af9b293ed15c70caf8e1c88 # v3.0.2
with:
secret-ids: |
CLOUDFLARE_API_TOKEN,textjs/${{ inputs.environment }}/CLOUDFLARE_API_TOKEN
parse-json-secrets: false

- name: Deploy to Cloudflare Pages
id: deploy
working-directory: apps/web
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_API_TOKEN: ${{ env.CLOUDFLARE_API_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
ENVIRONMENT: ${{ inputs.environment }}
PAGES_BRANCH: ${{ inputs.pages-branch }}
WRANGLER_OUTPUT_FILE_PATH: ${{ runner.temp }}/wrangler-output.json
run: |
if [ -z "$CLOUDFLARE_API_TOKEN" ] || [ -z "$CLOUDFLARE_ACCOUNT_ID" ]; then
echo "::error::Set CLOUDFLARE_API_TOKEN and CLOUDFLARE_ACCOUNT_ID in the $ENVIRONMENT environment to deploy."
echo "::error::Set textjs/<stage>/CLOUDFLARE_API_TOKEN in AWS Secrets Manager and the CLOUDFLARE_ACCOUNT_ID variable on the $ENVIRONMENT environment to deploy."
exit 1
fi
message=$(git log -1 --format=%s | cut -c1-200)
Expand Down Expand Up @@ -151,17 +167,12 @@ jobs:
exit 1
fi

- name: Check for a Workers token
# The curl Worker's token lives in AWS (textjs/production/CLOUDFLARE_WORKERS_TOKEN),
# so every production deploy carries it. A missing secret fails the curl-worker job.
- name: Queue the curl Worker deploy
id: worker
if: inputs.environment == 'production'
env:
HAS_WORKERS_TOKEN: ${{ secrets.CLOUDFLARE_WORKERS_TOKEN != '' }}
run: |
if [ "$HAS_WORKERS_TOKEN" = true ]; then
echo "deploy=true" >> "$GITHUB_OUTPUT"
else
echo "::notice::CLOUDFLARE_WORKERS_TOKEN isn't set in the production environment. Skipping the curl Worker deploy."
fi
run: echo "deploy=true" >> "$GITHUB_OUTPUT"

curl-worker:
name: curl Worker
Expand All @@ -172,6 +183,9 @@ jobs:
environment:
name: production
url: http://textjs.org/
permissions:
contents: read
id-token: write # assume textjs-production
steps:
- name: Refuse stale reruns
if: github.run_attempt > 1
Expand All @@ -192,11 +206,24 @@ jobs:
bun-version: 1.4.4
no-cache: true

- name: Configure AWS credentials
uses: aws-actions/configure-aws-credentials@e1253824e5c10ff9df46874f81ed3ec929e19cfd # v6.3.0
with:
role-to-assume: arn:aws:iam::604439173067:role/github/textjs-production
role-session-name: textjs-workers-secrets-${{ github.run_id }}
aws-region: us-east-1
- name: Fetch the curl Worker secret
uses: aws-actions/aws-secretsmanager-get-secrets@2297f9a879480a9e3af9b293ed15c70caf8e1c88 # v3.0.2
with:
secret-ids: |
CLOUDFLARE_WORKERS_TOKEN,textjs/production/CLOUDFLARE_WORKERS_TOKEN
parse-json-secrets: false

- name: Deploy textjs-curl-http
working-directory: apps/web/workers/curl-http
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_WORKERS_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
CLOUDFLARE_API_TOKEN: ${{ env.CLOUDFLARE_WORKERS_TOKEN }}
CLOUDFLARE_ACCOUNT_ID: ${{ vars.CLOUDFLARE_ACCOUNT_ID }}
run: |
bunx "wrangler@$WRANGLER_VERSION" deploy --message "$(git log -1 --format=%s | cut -c1-200)"
echo "### ▼ curl Worker deployed to http://textjs.org/" >> "$GITHUB_STEP_SUMMARY"
19 changes: 10 additions & 9 deletions .github/workflows/deploy.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,13 @@
# cancels a running preview deploy.
# - Pull requests never deploy; CI covers them without secrets.
#
# The steps live in deploy-site.yml. Secrets come from the GitHub environment
# each run deploys to:
# - `production` and `dev`: CLOUDFLARE_API_TOKEN (Account → Cloudflare Pages →
# Edit) and CLOUDFLARE_ACCOUNT_ID.
# - `production` only, optional: CLOUDFLARE_WORKERS_TOKEN, which deploys the curl
# Worker in apps/web/workers/curl-http after every production deploy.
# The steps live in deploy-site.yml. Secrets come from AWS Secrets Manager,
# read with the AWS role of the GitHub environment each run deploys to:
# - `production` and `dev`: textjs/<environment>/CLOUDFLARE_API_TOKEN (Account →
# Cloudflare Pages → Edit), assuming github/textjs-<environment>. The account
# id is the CLOUDFLARE_ACCOUNT_ID environment variable.
# - `production` only: textjs/production/CLOUDFLARE_WORKERS_TOKEN, which deploys
# the curl Worker in apps/web/workers/curl-http after every production deploy.

name: Deploy

Expand All @@ -23,6 +24,9 @@ on:

permissions:
contents: read
# The called workflow's jobs assume an AWS role; a called workflow cannot be
# granted more than its caller, so the permission is declared here too.
id-token: write

jobs:
production:
Expand All @@ -36,8 +40,6 @@ jobs:
with:
environment: production
pages-branch: main
# A called workflow gets no secrets, environment secrets included, unless the caller passes them.
secrets: inherit

dev:
name: Preview
Expand All @@ -49,4 +51,3 @@ jobs:
with:
environment: dev
pages-branch: dev
secrets: inherit
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),

## [Unreleased]

## [1.6.1] - 2026-11-06

### Fixed

- `@textjs/app` (experimental): a visitor who clicks back into the page from DevTools while an embedded app is still downloading gets its keys with their next click on the page. Before, the app missed that return, and clicks on the page never reached it again until focus went to DevTools and back. `onConsoleIntent`'s event has `away()`, which says whether focus has left the page since the intent, and `createHostInput` takes it as `away`. `embed.js` passes it on by itself.

## [1.6.0] - 2026-11-05

### Added
Expand Down
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

# ▼ Text.js

<a href="https://opencode.ai"><picture><source media="(prefers-color-scheme: dark)" srcset="https://opencode.ai/brand/badges/opencode-built-with-g-dark-20.svg"><img alt="Built with OpenCode" src="https://opencode.ai/brand/badges/opencode-built-with-g-light-20.svg" height="20" /></picture></a>

Text.js is a React framework for apps that render in the browser console.

[Docs](https://textjs.org/docs) · [Learn](https://textjs.org/learn) · [Playground](https://textjs.org/playground) · [OpenCode](https://textjs.org/opencode)
Expand Down
2 changes: 1 addition & 1 deletion apps/opencode-console/scripts/key-catalog.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"""Vendor the documented binding defaults (including disabled commands) without shipping CLI code."""
import json,re,sys
from pathlib import Path
source=Path(sys.argv[1])/'services/www/src/docs/content/cli/keybinds.mdx'
source=Path(sys.argv[1])/'services/site/src/docs/content/cli/keybinds.mdx'
catalog={}
for line in source.read_text().splitlines():
if line.startswith('## '): group=line[3:]
Expand Down
2 changes: 1 addition & 1 deletion apps/opencode-console/scripts/keybind-ledger.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import sys

reference = Path(sys.argv[1]).expanduser() if len(sys.argv) > 1 else Path.home()/'code/opencode'
source = reference/'services/www/src/docs/content/cli/keybinds.mdx'
source = reference/'services/site/src/docs/content/cli/keybinds.mdx'
revision = subprocess.check_output(['git', '-C', reference, 'rev-parse', '--short', 'HEAD'], text=True).strip()
supported = set('''leader command.palette.show help.show session.new session.list session.sidebar.toggle model.list
agent.list agent.cycle variant.cycle variant.list model.cycle_recent model.cycle_recent_reverse theme.switch
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/docs/content/embedding.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ When the files are gone, `embed.js` logs one warning that asks the visitor to re
| `startEmbed(paint, boot, src, element?)` | What `embed.js` runs: log the first frame, wait for console intent, then call `boot` |
| `bootEmbedClient(options)` | Start a client-rendered app with in-memory routing and keyboard input gated for the page |
| `bootEmbedPlayer(options)` | Start the frame player for a server-rendered route |
| `createHostInput(options)` | Keyboard input that follows the rules above |
| `createHostInput(options)` | Keyboard input that follows the rules above. `engaged` starts with the keys, and `away` (from the intent's `away()`) counts the next click on the page as the one back from DevTools |
| `createTextConsoleMount(config)` | Returns a `mount(element, shell?)` function for `<text-console>` |
| `reserveCss(cols, rows)` | The CSS that reserves `<text-console>`'s space |

Expand Down
2 changes: 2 additions & 0 deletions apps/web/src/docs/content/hooks-and-repl.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ onConsoleIntent(
);
```

`e.away()` says whether focus has left the page for DevTools since the intent. Pass it to `createHostInput({ away: e.away() })` when the app's input starts, so a visitor who clicked back into the page while the app downloaded gets its keys with their next click on the page.

DevTools opened in its own window from a browser menu doesn't show up in the window size. Those visitors start the app from an `engage` element, and `session` keeps it starting on reloads in that tab.

Nothing fires automatically on touch screens. Call `trigger()` from your own control.
Expand Down
16 changes: 8 additions & 8 deletions bun.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 2 additions & 0 deletions docs/readme/README.ar.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

# ▼ Text.js

<a href="https://opencode.ai"><picture><source media="(prefers-color-scheme: dark)" srcset="https://opencode.ai/brand/badges/opencode-built-with-g-dark-20.svg"><img alt="Built with OpenCode" src="https://opencode.ai/brand/badges/opencode-built-with-g-light-20.svg" height="20" /></picture></a>

<span dir="ltr">Text.js</span> هو إطار عمل React لتطبيقات تُعرض في وحدة التحكم في المتصفح.

[التوثيق](https://textjs.org/ar/docs) · [تعلّم](https://textjs.org/ar/learn) · [Playground](https://textjs.org/ar/playground) · [OpenCode](https://textjs.org/ar/opencode)
Expand Down
2 changes: 2 additions & 0 deletions docs/readme/README.de.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

# ▼ Text.js

<a href="https://opencode.ai"><picture><source media="(prefers-color-scheme: dark)" srcset="https://opencode.ai/brand/badges/opencode-built-with-g-dark-20.svg"><img alt="Built with OpenCode" src="https://opencode.ai/brand/badges/opencode-built-with-g-light-20.svg" height="20" /></picture></a>

Text.js ist ein React-Framework für Apps, die in der Browser-Konsole rendern.

[Dokumentation](https://textjs.org/de/docs) · [Lernen](https://textjs.org/de/learn) · [Playground](https://textjs.org/de/playground) · [OpenCode](https://textjs.org/de/opencode)
Expand Down
2 changes: 2 additions & 0 deletions docs/readme/README.es.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

# ▼ Text.js

<a href="https://opencode.ai"><picture><source media="(prefers-color-scheme: dark)" srcset="https://opencode.ai/brand/badges/opencode-built-with-g-dark-20.svg"><img alt="Built with OpenCode" src="https://opencode.ai/brand/badges/opencode-built-with-g-light-20.svg" height="20" /></picture></a>

Text.js es un framework de React para apps que se renderizan en la consola del navegador.

[Documentación](https://textjs.org/es/docs) · [Aprender](https://textjs.org/es/learn) · [Playground](https://textjs.org/es/playground) · [OpenCode](https://textjs.org/es/opencode)
Expand Down
2 changes: 2 additions & 0 deletions docs/readme/README.fa.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

# ▼ Text.js

<a href="https://opencode.ai"><picture><source media="(prefers-color-scheme: dark)" srcset="https://opencode.ai/brand/badges/opencode-built-with-g-dark-20.svg"><img alt="Built with OpenCode" src="https://opencode.ai/brand/badges/opencode-built-with-g-light-20.svg" height="20" /></picture></a>

<span dir="ltr">Text.js</span> یک فریم‌ورک React برای اپ‌هایی است که در کنسول مرورگر رندر می‌شوند.

[مستندات](https://textjs.org/fa/docs) · [آموزش](https://textjs.org/fa/learn) · [Playground](https://textjs.org/fa/playground) · [OpenCode](https://textjs.org/fa/opencode)
Expand Down
2 changes: 2 additions & 0 deletions docs/readme/README.fr.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

# ▼ Text.js

<a href="https://opencode.ai"><picture><source media="(prefers-color-scheme: dark)" srcset="https://opencode.ai/brand/badges/opencode-built-with-g-dark-20.svg"><img alt="Built with OpenCode" src="https://opencode.ai/brand/badges/opencode-built-with-g-light-20.svg" height="20" /></picture></a>

Text.js est un framework React pour les applications qui s’affichent dans la console du navigateur.

[Documentation](https://textjs.org/fr/docs) · [Apprendre](https://textjs.org/fr/learn) · [Playground](https://textjs.org/fr/playground) · [OpenCode](https://textjs.org/fr/opencode)
Expand Down
2 changes: 2 additions & 0 deletions docs/readme/README.he.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@

# ▼ Text.js

<a href="https://opencode.ai"><picture><source media="(prefers-color-scheme: dark)" srcset="https://opencode.ai/brand/badges/opencode-built-with-g-dark-20.svg"><img alt="Built with OpenCode" src="https://opencode.ai/brand/badges/opencode-built-with-g-light-20.svg" height="20" /></picture></a>

<span dir="ltr">Text.js</span> הוא פריימוורק React לאפליקציות שמרנדרות במסוף הדפדפן.

[תיעוד](https://textjs.org/he/docs) · [למידה](https://textjs.org/he/learn) · [Playground](https://textjs.org/he/playground) · [OpenCode](https://textjs.org/he/opencode)
Expand Down
2 changes: 2 additions & 0 deletions docs/readme/README.hi.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

# ▼ Text.js

<a href="https://opencode.ai"><picture><source media="(prefers-color-scheme: dark)" srcset="https://opencode.ai/brand/badges/opencode-built-with-g-dark-20.svg"><img alt="Built with OpenCode" src="https://opencode.ai/brand/badges/opencode-built-with-g-light-20.svg" height="20" /></picture></a>

Text.js ब्राउज़र कंसोल में रेंडर होने वाले ऐप्स के लिए एक React फ़्रेमवर्क है।

[डॉक्स](https://textjs.org/hi/docs) · [सीखें](https://textjs.org/hi/learn) · [Playground](https://textjs.org/hi/playground) · [OpenCode](https://textjs.org/hi/opencode)
Expand Down
2 changes: 2 additions & 0 deletions docs/readme/README.id.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

# ▼ Text.js

<a href="https://opencode.ai"><picture><source media="(prefers-color-scheme: dark)" srcset="https://opencode.ai/brand/badges/opencode-built-with-g-dark-20.svg"><img alt="Built with OpenCode" src="https://opencode.ai/brand/badges/opencode-built-with-g-light-20.svg" height="20" /></picture></a>

Text.js adalah framework React untuk aplikasi yang di-render di konsol browser.

[Dokumentasi](https://textjs.org/id/docs) · [Belajar](https://textjs.org/id/learn) · [Playground](https://textjs.org/id/playground) · [OpenCode](https://textjs.org/id/opencode)
Expand Down
2 changes: 2 additions & 0 deletions docs/readme/README.it.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

# ▼ Text.js

<a href="https://opencode.ai"><picture><source media="(prefers-color-scheme: dark)" srcset="https://opencode.ai/brand/badges/opencode-built-with-g-dark-20.svg"><img alt="Built with OpenCode" src="https://opencode.ai/brand/badges/opencode-built-with-g-light-20.svg" height="20" /></picture></a>

Text.js è un framework React per app che eseguono il rendering nella console del browser.

[Documentazione](https://textjs.org/it/docs) · [Impara](https://textjs.org/it/learn) · [Playground](https://textjs.org/it/playground) · [OpenCode](https://textjs.org/it/opencode)
Expand Down
2 changes: 2 additions & 0 deletions docs/readme/README.ja.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

# ▼ Text.js

<a href="https://opencode.ai"><picture><source media="(prefers-color-scheme: dark)" srcset="https://opencode.ai/brand/badges/opencode-built-with-g-dark-20.svg"><img alt="Built with OpenCode" src="https://opencode.ai/brand/badges/opencode-built-with-g-light-20.svg" height="20" /></picture></a>

Text.jsは、ブラウザーのコンソールに描画するアプリのためのReactフレームワークです。

[ドキュメント](https://textjs.org/ja/docs) · [学習](https://textjs.org/ja/learn) · [Playground](https://textjs.org/ja/playground) · [OpenCode](https://textjs.org/ja/opencode)
Expand Down
2 changes: 2 additions & 0 deletions docs/readme/README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

# ▼ Text.js

<a href="https://opencode.ai"><picture><source media="(prefers-color-scheme: dark)" srcset="https://opencode.ai/brand/badges/opencode-built-with-g-dark-20.svg"><img alt="Built with OpenCode" src="https://opencode.ai/brand/badges/opencode-built-with-g-light-20.svg" height="20" /></picture></a>

Text.js는 브라우저 콘솔에서 렌더링하는 앱을 위한 React 프레임워크예요.

[문서](https://textjs.org/ko/docs) · [학습](https://textjs.org/ko/learn) · [Playground](https://textjs.org/ko/playground) · [OpenCode](https://textjs.org/ko/opencode)
Expand Down
2 changes: 2 additions & 0 deletions docs/readme/README.pl.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

# ▼ Text.js

<a href="https://opencode.ai"><picture><source media="(prefers-color-scheme: dark)" srcset="https://opencode.ai/brand/badges/opencode-built-with-g-dark-20.svg"><img alt="Built with OpenCode" src="https://opencode.ai/brand/badges/opencode-built-with-g-light-20.svg" height="20" /></picture></a>

Text.js to framework React do aplikacji renderowanych w konsoli przeglądarki.

[Dokumentacja](https://textjs.org/pl/docs) · [Nauka](https://textjs.org/pl/learn) · [Playground](https://textjs.org/pl/playground) · [OpenCode](https://textjs.org/pl/opencode)
Expand Down
2 changes: 2 additions & 0 deletions docs/readme/README.pt-BR.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

# ▼ Text.js

<a href="https://opencode.ai"><picture><source media="(prefers-color-scheme: dark)" srcset="https://opencode.ai/brand/badges/opencode-built-with-g-dark-20.svg"><img alt="Built with OpenCode" src="https://opencode.ai/brand/badges/opencode-built-with-g-light-20.svg" height="20" /></picture></a>

Text.js é um framework React para apps que renderizam no console do navegador.

[Documentação](https://textjs.org/pt-br/docs) · [Aprender](https://textjs.org/pt-br/learn) · [Playground](https://textjs.org/pt-br/playground) · [OpenCode](https://textjs.org/pt-br/opencode)
Expand Down
Loading
Loading