diff --git a/.claude/skills/release-shinyreact/SKILL.md b/.claude/skills/release-shinyreact/SKILL.md index 3d00d63b..65bc06b5 100644 --- a/.claude/skills/release-shinyreact/SKILL.md +++ b/.claude/skills/release-shinyreact/SKILL.md @@ -11,7 +11,7 @@ does **not** oblige releasing the others. | Package | Version lives in | Tag | Automation | |---|---|---|---| | `shinyreact` (PyPI) | `pyproject.toml` (repo root) `version` | `py/v1.2.3` | `.github/workflows/release-py.yaml` | -| `@posit/shinyreact` (npm) | `pkg-js/package.json` `version` | `js/v1.2.3` | `.github/workflows/release-js.yaml` | +| `@posit-dev/shinyreact` (npm) | `pkg-js/package.json` `version` | `js/v1.2.3` | `.github/workflows/release-js.yaml` | | `shinyreact` (R) | `pkg-r/DESCRIPTION` `Version` | `r/v1.2.3` | none — CRAN is a manual submission | The `/v` prefix is what keeps the three apart; nothing else distinguishes @@ -93,5 +93,5 @@ produces the same tidyverse checklist. ## After any release Nothing is automated post-release. Check the artifact actually landed -(`pip index versions shinyreact`, `npm view @posit/shinyreact version`) and +(`pip index versions shinyreact`, `npm view @posit-dev/shinyreact version`) and say so — a green workflow with a skipped publish step is the usual failure. diff --git a/.claude/skills/shinyreact-build-app/SKILL.md b/.claude/skills/shinyreact-build-app/SKILL.md index 01822a76..0a922ace 100644 --- a/.claude/skills/shinyreact-build-app/SKILL.md +++ b/.claude/skills/shinyreact-build-app/SKILL.md @@ -102,7 +102,7 @@ root.render(); ``` Hooks come off the same global: `const { useShinyInput } = window.shinyreact;`. -(An npm-tier app that installs `@posit/shinyreact` imports them instead and +(An npm-tier app that installs `@posit-dev/shinyreact` imports them instead and externalizes React the same way; reach for that only when you are publishing a component library, not for an app.) @@ -242,7 +242,7 @@ identical; React-owned state is still the default for inputs. ## Step 4 — the client reads and writes named channels -Everything is on `window.shinyreact` (or imported from `@posit/shinyreact` in +Everything is on `window.shinyreact` (or imported from `@posit-dev/shinyreact` in an npm-tier build). | | Full | Read-only | Write-only | diff --git a/.github/workflows/release-js.yaml b/.github/workflows/release-js.yaml index a6135b7e..979abb7f 100644 --- a/.github/workflows/release-js.yaml +++ b/.github/workflows/release-js.yaml @@ -1,6 +1,6 @@ name: release-js -# Publishes @posit/shinyreact to npm. Trigger manually (workflow_dispatch) or +# Publishes @posit-dev/shinyreact to npm. Trigger manually (workflow_dispatch) or # by pushing a tag like js/v0.0.1 (the `js/` prefix distinguishes it from the # Python and R release tags). Requires the NPM_TOKEN repository secret. on: @@ -56,7 +56,7 @@ jobs: working-directory: pkg-js run: npm run build - - name: 🚀 Publish @posit/shinyreact + - name: 🚀 Publish @posit-dev/shinyreact working-directory: pkg-js run: npm publish --access public --provenance env: diff --git a/DESIGN.md b/DESIGN.md index b28567b8..87f192a0 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -140,7 +140,7 @@ my-app/ ui.css ``` -The author owns `app.py`. Everything else is generated by AI (Claude or equivalent), typically via a Claude Skill or similar tooling that understands Shiny's client-server bridge. The `package.json` declares dependencies on the Shiny client runtime (`@posit/shiny`), the bridge hooks (`@posit/shinyreact`), React, and any component libraries. The build step (managed by `shiny run` or equivalent) resolves dependencies and bundles `src/` into `dist/`. +The author owns `app.py`. Everything else is generated by AI (Claude or equivalent), typically via a Claude Skill or similar tooling that understands Shiny's client-server bridge. The `package.json` declares dependencies on the Shiny client runtime (`@posit/shiny`), the bridge hooks (`@posit-dev/shinyreact`), React, and any component libraries. The build step (managed by `shiny run` or equivalent) resolves dependencies and bundles `src/` into `dist/`. ### What the server file contains @@ -287,7 +287,7 @@ In ui.tsx-first apps, `shiny run` should explicitly disable HTMLDependency injec ### Shiny client runtime as an npm package -`shiny.js` and the bridge hooks need to be published as `@posit/shiny` and `@posit/shinyreact` so client bundlers can resolve them via `package.json` instead of HTMLDependency injection — replacing IIFE bundles, `window.shinyreact` globals, and ad-hoc React deduplication. Significant upstream ask to the Shiny team. Tracked in [#28](https://github.com/posit-dev/shinyreact/issues/28); the hybrid distribution plan (npm runtime alongside the HTMLDependency zero-build tier) is decided in `decisions/2026-08-17-js-distribution.md`. +`shiny.js` and the bridge hooks need to be published as `@posit/shiny` and `@posit-dev/shinyreact` so client bundlers can resolve them via `package.json` instead of HTMLDependency injection — replacing IIFE bundles, `window.shinyreact` globals, and ad-hoc React deduplication. Significant upstream ask to the Shiny team. Tracked in [#28](https://github.com/posit-dev/shinyreact/issues/28); the hybrid distribution plan (npm runtime alongside the HTMLDependency zero-build tier) is decided in `decisions/2026-08-17-js-distribution.md`. ### `shiny run` build integration diff --git a/FEATURES.md b/FEATURES.md index 60755611..83dc59be 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -74,7 +74,7 @@ R has no e2e suite, so no `(e2e)` leaf covers R (issue #194). role="alert">` is appended to `` carrying the same text as the thrown error (e2e) - literals in the message (both versions, `#shinyreact-config`, - `@posit/shinyreact`) are marked with backticks and render as `` + `@posit-dev/shinyreact`) are marked with backticks and render as `` chips; the backticks are stripped from the thrown error and the console-only warning (e2e) - the message is written with `textContent`, never `innerHTML`, so a @@ -109,7 +109,7 @@ R has no e2e suite, so no `(e2e)` leaf covers R (issue #194). - `[js]` whether a *missing* tag is fatal depends on which build is running - IIFE bundle (shipped inside the R/Python packages): tolerated, because a hand-wired `page_bare()` page legitimately has no tag - - npm ESM build (`@posit/shinyreact`): fatal, opted into at import time — + - npm ESM build (`@posit-dev/shinyreact`): fatal, opted into at import time — an independently installed client meeting a tagless page means the server predates the protocol - server → client custom message: `shinyReactMessage`, payload `{id, data}` @@ -649,7 +649,7 @@ registries are exposed on `window.Shiny.reactRegistry`; the message registry on ### `ShinyReactComponentElement` (not exported) -- not reachable from `window.shinyreact`, `@posit/shinyreact`, or +- not reachable from `window.shinyreact`, `@posit-dev/shinyreact`, or `shiny-react/index.ts`; the class and its unit test stay in the tree until partial React app support brings it back - a base class for custom elements that mount a React component with Shiny @@ -690,7 +690,7 @@ the shinyreact bundle dependency and the `#shinyreact-config` tag — except - it defaults to `"server"` — the package serves both as an `HTMLDependency` - `"client"` omits **both files**; the `#shinyreact-config` tag is still emitted, because the npm-tier client hard-errors without it -- it is for the npm tier: a client importing `@posit/shinyreact` bundles its own +- it is for the npm tier: a client importing `@posit-dev/shinyreact` bundles its own copy, so serving them too puts two copies of React and the hooks on the page - any other value raises, naming the bad value and both valid ones - `[py]` `ValueError`; `[r]` `cli_abort` @@ -1272,7 +1272,7 @@ initial page. assignment — nothing writes that namespace before the bundle runs - CSS is a side-effect import, which is how Vite bundles it - a missing `#shinyreact-config` tag is tolerated -- the npm ESM build (`@posit/shinyreact`) is for bundler-tier apps +- the npm ESM build (`@posit-dev/shinyreact`) is for bundler-tier apps - React and ReactDOM are **peer** dependencies resolved by the app's bundler, so dev builds get a development React with Fast Refresh - hooks are imported directly rather than read off `window.shinyreact` @@ -1283,7 +1283,7 @@ initial page. - it exports `PROTOCOL_VERSION` - it does **not** re-export React / ReactDOM - the stylesheet ships as `dist-npm/style.css`, reachable as - `@posit/shinyreact/styles` (#229; the build emitted no CSS at all before, + `@posit-dev/shinyreact/styles` (#229; the build emitted no CSS at all before, leaving `ImageOutput`'s spinner without its `@keyframes spin`) - Vite does not inject a CSS import into a lib-mode ESM bundle, so consumers opt in rather than having it forced on them diff --git a/decisions/2026-08-17-js-distribution.md b/decisions/2026-08-17-js-distribution.md index 67e2e255..968c2122 100644 --- a/decisions/2026-08-17-js-distribution.md +++ b/decisions/2026-08-17-js-distribution.md @@ -116,12 +116,12 @@ payoff); would ship React ESM builds inside pip/CRAN packages. ## Decision -Adopt **option E**, with the npm package named **`@posit/shinyreact`** (not +Adopt **option E**, with the npm package named **`@posit-dev/shinyreact`** (not `@posit/shiny-react` — name consistency with the Python/R packages; the upstream `@posit/shiny-react`@0.0.16 that `pkg-js/src/shiny-react/` was vendored from is a separate, frozen artifact). -1. **Publish `@posit/shinyreact` to npm as the real runtime** — the hooks and +1. **Publish `@posit-dev/shinyreact` to npm as the real runtime** — the hooks and components as ESM, with `react`/`react-dom` as peer dependencies. The bundler tier imports it directly and never touches the global. Dev mode gets a dev React and Fast Refresh with no bridge-alias workaround. Built @@ -140,7 +140,7 @@ from is a separate, frozen artifact). and gives the handshake its delivery vehicle. This change is independent of npm publishing and should land first. -4. **Semver protocol handshake at hook boot.** `@posit/shinyreact` declares a +4. **Semver protocol handshake at hook boot.** `@posit-dev/shinyreact` declares a supported protocol range (e.g. `^1`); on init it checks the server-rendered `protocolVersion` and fails fast with a message naming both versions and the fix. The protocol version bumps only when the wire format @@ -204,9 +204,9 @@ The rule, then: `global.ts` reading the tag instead of `_restore`. 2. Protocol schema document + cross-language fixture tests. 3. Package `pkg-js/src/` for dual output (ESM + IIFE); add publish workflow for - `@posit/shinyreact`. + `@posit-dev/shinyreact`. 4. Convert one Vite example (`09-hmr` is the natural candidate) to import - `@posit/shinyreact`; retire its dev/prod bridge alias. Done in #217, which + `@posit-dev/shinyreact`; retire its dev/prod bridge alias. Done in #217, which also added the `shinyreact_js="client"` page-entry-point switch the conversion needs — an npm-tier page must not *also* be served the IIFE bundle. diff --git a/docs/posit-conf-2026-goals.md b/docs/posit-conf-2026-goals.md index bfd5bde4..eac72234 100644 --- a/docs/posit-conf-2026-goals.md +++ b/docs/posit-conf-2026-goals.md @@ -45,7 +45,7 @@ Remaining known issues are tracked in the [GitHub issue tracker](https://github. The vendored `shiny-react` code at `pkg-js/src/shiny-react/` and the broader Shiny JS infrastructure should live in its own repository, independent of `shinyreact`. This enables: -- Other packages (not just `shinyreact`) to depend on the published `@posit/shinyreact` npm package (naming decided in `decisions/2026-08-17-js-distribution.md`) +- Other packages (not just `shinyreact`) to depend on the published `@posit-dev/shinyreact` npm package (naming decided in `decisions/2026-08-17-js-distribution.md`) - Independent versioning and release cycle - Cleaner separation of concerns between Shiny's JS plumbing and `shinyreact`'s rendering diff --git a/examples/09-hmr/FEATURES.md b/examples/09-hmr/FEATURES.md index c67a18f5..ea899dfe 100644 --- a/examples/09-hmr/FEATURES.md +++ b/examples/09-hmr/FEATURES.md @@ -20,7 +20,7 @@ a unit test; `(verify)` marks a claim not yet checked against the code. - builds `pkg-js` first (`npm install` + `npm run build` there) when `pkg-js/dist-npm` is missing — the `file:../../pkg-js` dep's `exports` point into `dist-npm/`, which is not committed, so this app's own build - cannot resolve `@posit/shinyreact` until it exists + cannot resolve `@posit-dev/shinyreact` until it exists - then `npm install` + `npm run build` in the app directory - without it, Shiny raises `RuntimeError: Directory '.../www' does not exist` at startup — `www/` itself does not exist until a build creates it @@ -30,7 +30,7 @@ a unit test; `(verify)` marks a claim not yet checked against the code. ## npm tier -- the only example that imports `@posit/shinyreact` instead of destructuring +- the only example that imports `@posit-dev/shinyreact` instead of destructuring `window.shinyreact`; the built `www/ui.js` contains no `window.shinyreact` reference at all - until the first npm publish, the dependency is `file:../../pkg-js`, so @@ -51,7 +51,7 @@ a unit test; `(verify)` marks a claim not yet checked against the code. - `www/ui.js` is gitignored, so a fresh checkout must run one of the two before `shiny run` serves anything - there is no dev/prod hook switch any more: both modes import - `@posit/shinyreact`, so no `shiny-bridge` alias and no rollup externals + `@posit-dev/shinyreact`, so no `shiny-bridge` alias and no rollup externals ## The dev stub (`vite-dev-stub.js`) diff --git a/examples/09-hmr/README.md b/examples/09-hmr/README.md index 04d23d15..6b5b42d4 100644 --- a/examples/09-hmr/README.md +++ b/examples/09-hmr/README.md @@ -5,7 +5,7 @@ serves the page: edit a component and it hot-swaps in place, keeping component state — no full page reload, no `vite build` wait. This is the **npm-tier** example: the client imports the hooks from -`@posit/shinyreact` and bundles its own React, in both modes. That is what +`@posit-dev/shinyreact` and bundles its own React, in both modes. That is what makes Fast Refresh possible — Fast Refresh needs a *development* React build, and the React inside the server-shipped IIFE bundle is production-only. @@ -14,10 +14,10 @@ Because the client ships the runtime itself, the server must not serve `set_react_page(shinyreact_js="client")` in `app.py`. The `#shinyreact-config` tag is still emitted, and the npm client requires it. -Until `@posit/shinyreact` is published, `package.json` depends on it as +Until `@posit-dev/shinyreact` is published, `package.json` depends on it as `file:../../pkg-js` — repo-relative, so it only resolves inside a checkout of this repo. Copy this example elsewhere and that line becomes -`"@posit/shinyreact": "^"` from npm. In the meantime the package has +`"@posit-dev/shinyreact": "^"` from npm. In the meantime the package has to be built before this example can build against it: ```bash diff --git a/examples/09-hmr/app.py b/examples/09-hmr/app.py index 1a8ff328..2764443f 100644 --- a/examples/09-hmr/app.py +++ b/examples/09-hmr/app.py @@ -12,7 +12,7 @@ _app_dir = Path(__file__).parent if not (_app_dir / "www" / "ui.js").exists(): print("www/ui.js not found -- building the client bundle...", file=sys.stderr) - # `@posit/shinyreact` is a `file:../../pkg-js` dep whose exports point at + # `@posit-dev/shinyreact` is a `file:../../pkg-js` dep whose exports point at # `dist-npm/`, which is not committed -- so the package has to be built # before this app's own build can resolve it. _pkg_js = _app_dir.parent.parent / "pkg-js" @@ -22,7 +22,7 @@ subprocess.run(["npm", "install"], cwd=_app_dir, check=True) subprocess.run(["npm", "run", "build"], cwd=_app_dir, check=True) -# npm tier: the client imports `@posit/shinyreact` and bundles shinyreact.js +# npm tier: the client imports `@posit-dev/shinyreact` and bundles shinyreact.js # itself, so the server must not serve it too -- two copies on one page. The # `#shinyreact-config` tag is still emitted, and the npm client requires it. set_react_page(shinyreact_js="client") diff --git a/examples/09-hmr/package.json b/examples/09-hmr/package.json index 98f115ad..5f2cb0d7 100644 --- a/examples/09-hmr/package.json +++ b/examples/09-hmr/package.json @@ -8,7 +8,7 @@ "test": "node --test" }, "dependencies": { - "@posit/shinyreact": "file:../../pkg-js" + "@posit-dev/shinyreact": "file:../../pkg-js" }, "devDependencies": { "@vitejs/plugin-react": "^4.3.0", diff --git a/examples/09-hmr/src/App.tsx b/examples/09-hmr/src/App.tsx index 7083d91b..97384dd5 100644 --- a/examples/09-hmr/src/App.tsx +++ b/examples/09-hmr/src/App.tsx @@ -1,6 +1,6 @@ import { useEffect, useState } from "react"; -import { useShinyInitialized, useShinyInput, useShinyOutputValue } from "@posit/shinyreact"; +import { useShinyInitialized, useShinyInput, useShinyOutputValue } from "@posit-dev/shinyreact"; // Exported component = a Fast Refresh boundary. Editing this file (e.g. the // heading text below) hot-swaps the component WITHOUT losing `count`. diff --git a/examples/09-hmr/vite.config.js b/examples/09-hmr/vite.config.js index 28abe8cf..85bcc747 100644 --- a/examples/09-hmr/vite.config.js +++ b/examples/09-hmr/vite.config.js @@ -21,7 +21,7 @@ export default defineConfig(({ command }) => ({ shinyreactDevStub({ entry: ENTRY, outFile: "www/ui.js" }), ], resolve: { - // `@posit/shinyreact` is a `file:` dep, so it is symlinked and brings its + // `@posit-dev/shinyreact` is a `file:` dep, so it is symlinked and brings its // own node_modules. Without dedupe, App.tsx and the hooks would each get a // React copy and every hook call would throw. dedupe: ["react", "react-dom"], @@ -29,7 +29,7 @@ export default defineConfig(({ command }) => ({ server: { port: 5173, strictPort: true, // keep the stub's hard-coded :5173 honest - // `@posit/shinyreact` resolves through a symlink to the repo's pkg-js/. + // `@posit-dev/shinyreact` resolves through a symlink to the repo's pkg-js/. fs: { allow: [repoRoot] }, }, build: { diff --git a/examples/README.md b/examples/README.md index ec9e2564..aa888896 100644 --- a/examples/README.md +++ b/examples/README.md @@ -32,7 +32,7 @@ see [Example behavior trees](#example-behavior-trees) below. | [06-data-frame](06-data-frame/) | Embeds `@render.data_frame` via `ShinyOutput` and `set_react_page()` | | [07-plotly](07-plotly/) | Embeds `@render_plotly` via `ShinyOutput` and `set_react_page()`. Also ships an `app.R` using `plotly::renderPlotly()` over the same `www/` client — its binding JS is discovered from the render function and pushed automatically | | [08-input-handler](08-input-handler/) | `useShinyInput` with `type="shiny.datetime"` — client sends unix seconds; server `input.when()` is a `datetime.datetime` via Shiny's built-in handler | -| [09-hmr](09-hmr/) | React Fast Refresh in dev (Vite dev server alongside Shiny). The npm tier: imports `@posit/shinyreact` and bundles its own React, with `set_react_page(shinyreact_js="client")` so the server doesn't also serve shinyreact.js | +| [09-hmr](09-hmr/) | React Fast Refresh in dev (Vite dev server alongside Shiny). The npm tier: imports `@posit-dev/shinyreact` and bundles its own React, with `set_react_page(shinyreact_js="client")` so the server doesn't also serve shinyreact.js | | [10-bookmarking](10-bookmarking/) | Bookmark restoration: URL query string (or server-stored state) hydrates `useShinyInput` initial values via the `#shinyreact-config` tag emitted by `page_react()` | ## Running an example diff --git a/pkg-js/package-lock.json b/pkg-js/package-lock.json index b5cd5b66..05d0c0e6 100644 --- a/pkg-js/package-lock.json +++ b/pkg-js/package-lock.json @@ -1,11 +1,11 @@ { - "name": "@posit/shinyreact", + "name": "@posit-dev/shinyreact", "version": "0.0.1", "lockfileVersion": 3, "requires": true, "packages": { "": { - "name": "@posit/shinyreact", + "name": "@posit-dev/shinyreact", "version": "0.0.1", "license": "MIT", "devDependencies": { diff --git a/pkg-js/package.json b/pkg-js/package.json index 91151b1f..2449dcbb 100644 --- a/pkg-js/package.json +++ b/pkg-js/package.json @@ -1,5 +1,5 @@ { - "name": "@posit/shinyreact", + "name": "@posit-dev/shinyreact", "version": "0.0.1", "description": "React hooks and components for Shiny (shinyreact) — the ui.tsx pattern's client runtime", "license": "MIT", diff --git a/pkg-js/src/npm.ts b/pkg-js/src/npm.ts index 39a38a06..eb625db0 100644 --- a/pkg-js/src/npm.ts +++ b/pkg-js/src/npm.ts @@ -1,5 +1,5 @@ /** - * `@posit/shinyreact` — the ESM entry for bundler-tier apps. + * `@posit-dev/shinyreact` — the ESM entry for bundler-tier apps. * * Import the hooks directly instead of reading `window.shinyreact`; React and * ReactDOM are peer dependencies resolved by your bundler (so dev builds get @@ -32,7 +32,7 @@ import "./shinyreact.css"; import { installDepDiscovery } from "./dep-discovery"; import { requireShinyReactConfigTag } from "./shiny-react/config"; -// Two copies on one page: this app bundles `@posit/shinyreact` AND the server +// Two copies on one page: this app bundles `@posit-dev/shinyreact` AND the server // served shinyreact.js, because the page entry point left `shinyreact_js` at its // default of "server". It still works — the registries are page-scoped, so the // two copies share one set of inputs, outputs, and message handlers — but the @@ -50,7 +50,7 @@ if ( ) { console.warn( "[shinyreact] shinyreact.js is loaded twice on this page: the server " + - "served it, and this app also imports @posit/shinyreact. The app works, " + + "served it, and this app also imports @posit-dev/shinyreact. The app works, " + "but it is downloading a second copy of React and the hooks for " + 'nothing. Pass shinyreact_js="client" to your page entry point — ' + "page_react(), page_react_html(), set_react_page(), or ReactApp().", diff --git a/pkg-js/src/shiny-react/bookmark.ts b/pkg-js/src/shiny-react/bookmark.ts index 15a2d82b..f63ca9d2 100644 --- a/pkg-js/src/shiny-react/bookmark.ts +++ b/pkg-js/src/shiny-react/bookmark.ts @@ -46,7 +46,7 @@ export function applyRestoredValues(registry: InputRegistry): void { if (config == null && isShinyReactConfigTagRequired()) { throwVisibly( "shinyreact: no `#shinyreact-config` tag found in this page. The " + - "`@posit/shinyreact` client requires a shinyreact server recent " + + "`@posit-dev/shinyreact` client requires a shinyreact server recent " + "enough to emit it — upgrade the shinyreact Python/R package.", ); } diff --git a/pkg-js/typedoc.json b/pkg-js/typedoc.json index 3cd815a5..fc6ae860 100644 --- a/pkg-js/typedoc.json +++ b/pkg-js/typedoc.json @@ -1,6 +1,6 @@ { "$schema": "https://typedoc.org/schema.json", - "name": "@posit/shinyreact", + "name": "@posit-dev/shinyreact", "entryPoints": ["src/npm.ts"], "tsconfig": "tsconfig.npm.json", "out": "../pkg-py/docs/_site/js", diff --git a/pkg-js/vite.config.npm.ts b/pkg-js/vite.config.npm.ts index 99dee6bc..fd2c738a 100644 --- a/pkg-js/vite.config.npm.ts +++ b/pkg-js/vite.config.npm.ts @@ -5,7 +5,7 @@ import { defineConfig } from "vite"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -// The @posit/shinyreact npm build: ESM, React externalized as a peer +// The @posit-dev/shinyreact npm build: ESM, React externalized as a peer // dependency (so app bundlers resolve a single React — a dev build in dev // mode). The IIFE build (vite.config.ts) inlines React instead; both are // built from the same source. Type declarations come from tsconfig.npm.json. diff --git a/pkg-py/docs/articles/hooks.qmd b/pkg-py/docs/articles/hooks.qmd index d7c77676..62b8d0bd 100644 --- a/pkg-py/docs/articles/hooks.qmd +++ b/pkg-py/docs/articles/hooks.qmd @@ -3,7 +3,7 @@ title: "Client hooks" --- The page entry points serve `shinyreact.js`, which bundles React 19 and installs the client API at `window.shinyreact`. -Apps with a bundler can instead `npm install @posit/shinyreact` and pass `shinyreact_js="client"` to the page entry point. +Apps with a bundler can instead `npm install @posit-dev/shinyreact` and pass `shinyreact_js="client"` to the page entry point. ## Hooks diff --git a/pkg-py/docs/articles/tsx-and-build-tools.qmd b/pkg-py/docs/articles/tsx-and-build-tools.qmd index b0f4a585..8b965ed9 100644 --- a/pkg-py/docs/articles/tsx-and-build-tools.qmd +++ b/pkg-py/docs/articles/tsx-and-build-tools.qmd @@ -192,7 +192,7 @@ Gitignore `node_modules/` and, if you like, the generated `www/ui.js` (the examp ### npm tier, bundling your own React For hot reloading with React Fast Refresh you need a *development* build of React, and the one inside `shinyreact.js` is production-only. -The [09-hmr](https://github.com/posit-dev/shinyreact/tree/main/examples/09-hmr) example therefore installs `@posit/shinyreact` from npm, imports the hooks from it, and bundles its own React with no externals. +The [09-hmr](https://github.com/posit-dev/shinyreact/tree/main/examples/09-hmr) example therefore installs `@posit-dev/shinyreact` from npm, imports the hooks from it, and bundles its own React with no externals. Because the client now ships React itself, the server must stop serving `shinyreact.js`, or the page would have two copies again. That is what `shinyreact_js="client"` on the page entry point does. @@ -206,7 +206,7 @@ Checking happens in your editor (VS Code and Positron do it automatically for an npx tsc --noEmit ``` -`@posit/shinyreact` ships type declarations, so `useShinyOutputValue("dist_data")` tells the checker what shape to expect and catches a misspelled field before the browser does. +`@posit-dev/shinyreact` ships type declarations, so `useShinyOutputValue("dist_data")` tells the checker what shape to expect and catches a misspelled field before the browser does. If you are not using TypeScript, skip all of this and name your files `.jsx`. ## Where to go next diff --git a/pkg-py/docs/index.qmd b/pkg-py/docs/index.qmd index 2b18f20e..f126ce1b 100644 --- a/pkg-py/docs/index.qmd +++ b/pkg-py/docs/index.qmd @@ -90,7 +90,7 @@ pak::pak("posit-dev/shinyreact/pkg-r") ::: The JS hooks come with the server package — `set_react_page()` / `page_react()` serve `shinyreact.js`, which installs them at `window.shinyreact`. -Apps with a bundler can instead `npm install @posit/shinyreact` and pass `shinyreact_js="client"` to the page entry point. +Apps with a bundler can instead `npm install @posit-dev/shinyreact` and pass `shinyreact_js="client"` to the page entry point. ## Agent Skills diff --git a/pkg-py/src/shinyreact/.agents/skills/shinyreact-build-app/SKILL.md b/pkg-py/src/shinyreact/.agents/skills/shinyreact-build-app/SKILL.md index 01822a76..0a922ace 100644 --- a/pkg-py/src/shinyreact/.agents/skills/shinyreact-build-app/SKILL.md +++ b/pkg-py/src/shinyreact/.agents/skills/shinyreact-build-app/SKILL.md @@ -102,7 +102,7 @@ root.render(); ``` Hooks come off the same global: `const { useShinyInput } = window.shinyreact;`. -(An npm-tier app that installs `@posit/shinyreact` imports them instead and +(An npm-tier app that installs `@posit-dev/shinyreact` imports them instead and externalizes React the same way; reach for that only when you are publishing a component library, not for an app.) @@ -242,7 +242,7 @@ identical; React-owned state is still the default for inputs. ## Step 4 — the client reads and writes named channels -Everything is on `window.shinyreact` (or imported from `@posit/shinyreact` in +Everything is on `window.shinyreact` (or imported from `@posit-dev/shinyreact` in an npm-tier build). | | Full | Read-only | Write-only | diff --git a/pkg-py/src/shinyreact/_app.py b/pkg-py/src/shinyreact/_app.py index 2febd6d7..3ab13c06 100644 --- a/pkg-py/src/shinyreact/_app.py +++ b/pkg-py/src/shinyreact/_app.py @@ -96,7 +96,7 @@ class ReactApp(_ShinyApp): provides; a static ``ui=page_react_html(...)`` raises. shinyreact_js: Who supplies ``shinyreact.js`` / ``shinyreact.css`` to the discovered UI: ``"server"`` (default) or ``"client"`` for an - npm-tier app whose bundle imports ``@posit/shinyreact``. Ignored + npm-tier app whose bundle imports ``@posit-dev/shinyreact``. Ignored when ``ui=`` is passed — build that UI with ``shinyreact_js="client"`` yourself. **kwargs: Forwarded to :class:`shiny.App` (``debug=``, ``test_mode=``). diff --git a/pkg-py/src/shinyreact/_dep.py b/pkg-py/src/shinyreact/_dep.py index 849606bf..f102b317 100644 --- a/pkg-py/src/shinyreact/_dep.py +++ b/pkg-py/src/shinyreact/_dep.py @@ -49,7 +49,7 @@ def _serves_bundle(shinyreact_js: ShinyreactJs) -> bool: f"shinyreact_js={shinyreact_js!r} is not valid. Expected one of " f'{expected}. Use "server" when the shinyreact package should serve ' "shinyreact.js (the default, and what a no-build app needs), and " - '"client" when your own bundle imports @posit/shinyreact and ' + '"client" when your own bundle imports @posit-dev/shinyreact and ' "therefore ships its own copy." ) return shinyreact_js == "server" diff --git a/pkg-py/src/shinyreact/_page.py b/pkg-py/src/shinyreact/_page.py index c6a0f5e7..f32fc555 100644 --- a/pkg-py/src/shinyreact/_page.py +++ b/pkg-py/src/shinyreact/_page.py @@ -145,7 +145,7 @@ def page_react( - ``"server"`` (default) — the shinyreact package serves them as an :class:`~htmltools.HTMLDependency`. What a no-build app needs, and what makes ``window.shinyreact`` exist. - - ``"client"`` — your own bundle imports ``@posit/shinyreact`` and + - ``"client"`` — your own bundle imports ``@posit-dev/shinyreact`` and ships its own copy, so the server sends nothing. Serving them too would put two copies of React and the hooks on one page. @@ -409,7 +409,7 @@ def set_react_page( exists, else discovers ``www/ui.js`` / ``www/ui.css``. shinyreact_js: Who supplies ``shinyreact.js`` / ``shinyreact.css``: ``"server"`` (default) or ``"client"`` for an npm-tier app whose - bundle imports ``@posit/shinyreact`` — see :func:`page_react`. + bundle imports ``@posit-dev/shinyreact`` — see :func:`page_react`. """ # Validate now rather than at first page render: a typo should fail at # startup, next to the call that made it. @@ -482,7 +482,7 @@ def page_react_html( ``window.shinyreact`` existing. shinyreact_js: Who supplies ``shinyreact.js`` / ``shinyreact.css``: ``"server"`` (default) or ``"client"`` for an npm-tier app whose - bundle imports ``@posit/shinyreact`` — see :func:`page_react`. + bundle imports ``@posit-dev/shinyreact`` — see :func:`page_react`. """ path = Path(path) if path.is_absolute(): diff --git a/pkg-r/R/dep.R b/pkg-r/R/dep.R index 299fbc39..bd3307a2 100644 --- a/pkg-r/R/dep.R +++ b/pkg-r/R/dep.R @@ -55,7 +55,7 @@ serves_bundle <- function(shinyreact_js) { "i" = "{.val server} (the default) serves {.file shinyreact.js} from the shinyreact package -- what a no-build app needs.", "i" = "{.val client} is for an app whose own bundle imports - {.pkg @posit/shinyreact} and therefore ships its own copy." + {.pkg @posit-dev/shinyreact} and therefore ships its own copy." )) } identical(shinyreact_js, "server") diff --git a/pkg-r/R/page.R b/pkg-r/R/page.R index 5c2ed3a7..64312f41 100644 --- a/pkg-r/R/page.R +++ b/pkg-r/R/page.R @@ -84,7 +84,7 @@ no_bootstrap <- function() { #' the page. `"server"` (the default) serves them from the shinyreact package #' as an [htmltools::htmlDependency] — what a no-build app needs, and what #' makes `window.shinyreact` exist. `"client"` is for an app whose own bundle -#' imports `@posit/shinyreact` and therefore ships its own copy; serving them +#' imports `@posit-dev/shinyreact` and therefore ships its own copy; serving them #' too would put two copies of React and the hooks on one page. The #' `#shinyreact-config` tag is emitted either way; the npm-tier client #' hard-errors without it. Mirrors Python's `page_react(shinyreact_js=)`. @@ -186,7 +186,7 @@ page_react <- function( #' `page_react_html(extra_deps=)`. #' @param shinyreact_js Who supplies `shinyreact.js` / `shinyreact.css`: #' `"server"` (the default) or `"client"` for an npm-tier app whose bundle -#' imports `@posit/shinyreact` — see [page_react()]. +#' imports `@posit-dev/shinyreact` — see [page_react()]. #' @return UI suitable for `shinyApp(ui = ...)`. #' @export page_react_html <- function( diff --git a/pkg-r/inst/skills/shinyreact-build-app/SKILL.md b/pkg-r/inst/skills/shinyreact-build-app/SKILL.md index 01822a76..0a922ace 100644 --- a/pkg-r/inst/skills/shinyreact-build-app/SKILL.md +++ b/pkg-r/inst/skills/shinyreact-build-app/SKILL.md @@ -102,7 +102,7 @@ root.render(); ``` Hooks come off the same global: `const { useShinyInput } = window.shinyreact;`. -(An npm-tier app that installs `@posit/shinyreact` imports them instead and +(An npm-tier app that installs `@posit-dev/shinyreact` imports them instead and externalizes React the same way; reach for that only when you are publishing a component library, not for an app.) @@ -242,7 +242,7 @@ identical; React-owned state is still the default for inputs. ## Step 4 — the client reads and writes named channels -Everything is on `window.shinyreact` (or imported from `@posit/shinyreact` in +Everything is on `window.shinyreact` (or imported from `@posit-dev/shinyreact` in an npm-tier build). | | Full | Read-only | Write-only | diff --git a/pkg-r/man/page_react.Rd b/pkg-r/man/page_react.Rd index c5be9e75..a22a32b8 100644 --- a/pkg-r/man/page_react.Rd +++ b/pkg-r/man/page_react.Rd @@ -34,7 +34,7 @@ that resolves to nothing usable.} the page. \code{"server"} (the default) serves them from the shinyreact package as an \link[htmltools:htmlDependency]{htmltools::htmlDependency} — what a no-build app needs, and what makes \code{window.shinyreact} exist. \code{"client"} is for an app whose own bundle -imports \verb{@posit/shinyreact} and therefore ships its own copy; serving them +imports \verb{@posit-dev/shinyreact} and therefore ships its own copy; serving them too would put two copies of React and the hooks on one page. The \verb{#shinyreact-config} tag is emitted either way; the npm-tier client hard-errors without it. Mirrors Python's \code{page_react(shinyreact_js=)}.} diff --git a/pkg-r/man/page_react_html.Rd b/pkg-r/man/page_react_html.Rd index c5896466..82d55936 100644 --- a/pkg-r/man/page_react_html.Rd +++ b/pkg-r/man/page_react_html.Rd @@ -26,7 +26,7 @@ they can rely on \code{window.shinyreact} existing. Mirrors Python's \item{shinyreact_js}{Who supplies \code{shinyreact.js} / \code{shinyreact.css}: \code{"server"} (the default) or \code{"client"} for an npm-tier app whose bundle -imports \verb{@posit/shinyreact} — see \code{\link[=page_react]{page_react()}}.} +imports \verb{@posit-dev/shinyreact} — see \code{\link[=page_react]{page_react()}}.} } \value{ UI suitable for \code{shinyApp(ui = ...)}. diff --git a/protocol/README.md b/protocol/README.md index 20a56782..99fef732 100644 --- a/protocol/README.md +++ b/protocol/README.md @@ -4,7 +4,7 @@ This document is the versioned contract between a shinyreact server (the Python or R package) and a shinyreact client (the IIFE bundle or the -`@posit/shinyreact` npm package). Its version — not any package version — is +`@posit-dev/shinyreact` npm package). Its version — not any package version — is what the client asserts at boot, so client and server packages can release independently as long as they speak the same protocol major version. Decided in `decisions/2026-08-17-js-distribution.md`. @@ -116,6 +116,6 @@ versions: - same major → proceed (minor differences are additive by policy above); - different major → throw, naming both versions and which side to upgrade; - tag absent → the IIFE bundle proceeds (it cannot skew); the - `@posit/shinyreact` npm build treats absence as an error, since an + `@posit-dev/shinyreact` npm build treats absence as an error, since an independently-installed client meeting a tag-less page means the server predates the protocol.