@octanejs/cli 0.0.3 → 0.0.5
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/README.md +1 -0
- package/package.json +2 -2
- package/src/commands/init/index.js +87 -6
- package/src/commands/init/templates.js +452 -9
- package/src/data/octane-data.json +75 -57
package/README.md
CHANGED
|
@@ -19,6 +19,7 @@ pnpm dlx @octanejs/cli doctor
|
|
|
19
19
|
|
|
20
20
|
| Command | What it does |
|
|
21
21
|
| --- | --- |
|
|
22
|
+
| `octane create [dir]` | Create an Octane app in a new directory. This is what `npm create octane` runs. |
|
|
22
23
|
| `octane init` | Wire Octane into the project in this directory: bundler plugin, tsconfig, scripts, dependencies. |
|
|
23
24
|
| `octane doctor` | Check the project for the mistakes that break Octane quietly. `--fix` repairs the mechanical ones. |
|
|
24
25
|
| `octane analyze` | Compile the project and report every Octane compiler diagnostic, with its code and suggested edit. |
|
package/package.json
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@octanejs/cli",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"engines": {
|
|
6
|
-
"node": ">=22"
|
|
6
|
+
"node": ">=22.22.2"
|
|
7
7
|
},
|
|
8
8
|
"description": "The Octane command line: diagnose, configure, and extend an Octane project.",
|
|
9
9
|
"license": "MIT",
|
|
@@ -12,8 +12,12 @@ import {
|
|
|
12
12
|
VITE_SCRIPTS,
|
|
13
13
|
appComponent,
|
|
14
14
|
clientEntry,
|
|
15
|
+
counterComponent,
|
|
16
|
+
globalStyles,
|
|
17
|
+
healthRoute,
|
|
15
18
|
indexHtml,
|
|
16
19
|
integrationFor,
|
|
20
|
+
layoutComponent,
|
|
17
21
|
octaneConfig,
|
|
18
22
|
prettierConfig,
|
|
19
23
|
tsconfig,
|
|
@@ -221,7 +225,7 @@ function plan(project, mode, integration) {
|
|
|
221
225
|
if (mode === 'fullstack' && !project.octaneConfigPath) {
|
|
222
226
|
changes.push({
|
|
223
227
|
file: 'octane.config.ts',
|
|
224
|
-
summary: 'create, with
|
|
228
|
+
summary: 'create, with routes for /, /counter, and GET /api/health',
|
|
225
229
|
apply: writeFile(at('octane.config.ts'), octaneConfig),
|
|
226
230
|
});
|
|
227
231
|
}
|
|
@@ -262,21 +266,98 @@ function plan(project, mode, integration) {
|
|
|
262
266
|
}
|
|
263
267
|
}
|
|
264
268
|
|
|
265
|
-
//
|
|
266
|
-
//
|
|
269
|
+
// The pages belong to the routes this run declares, so they follow the
|
|
270
|
+
// octane.config.ts written above and are not written into a project that
|
|
271
|
+
// brought its own. That config names its own entries, which may not be
|
|
272
|
+
// these files at all; writing them there produces components nothing
|
|
273
|
+
// routes to, and a shared layout whose nav links 404 because the routes
|
|
274
|
+
// behind them were never declared. A missing entry in someone's own config
|
|
275
|
+
// is a real problem, but it is `octane doctor`'s to report, not this
|
|
276
|
+
// command's to paper over with a page they did not ask for.
|
|
277
|
+
const scaffoldsRoutes = mode === 'fullstack' && !project.octaneConfigPath;
|
|
278
|
+
// spa needs the component only alongside the entry that imports it, so a
|
|
267
279
|
// project with its own page does not collect an orphan component. That is
|
|
268
280
|
// the entry this run writes, not the shell: keeping someone's src/main.ts
|
|
269
281
|
// means keeping whatever it imports, which is not this file.
|
|
270
|
-
|
|
271
|
-
|
|
282
|
+
const writesApp = (scaffoldsRoutes || writesClientEntry) && !existsSync(at('src/App.tsrx'));
|
|
283
|
+
const writesCounter = scaffoldsRoutes && !existsSync(at('src/Counter.tsrx'));
|
|
284
|
+
// Both fullstack pages import the layout, so it follows whichever of them
|
|
285
|
+
// this run writes: writing one of those pages without it would scaffold an
|
|
286
|
+
// app whose own import does not resolve.
|
|
287
|
+
const writesLayout =
|
|
288
|
+
mode === 'fullstack' && (writesApp || writesCounter) && !existsSync(at('src/Layout.tsrx'));
|
|
289
|
+
|
|
290
|
+
// The components this run scaffolds, in the order the app reads: the entry,
|
|
291
|
+
// the frame it renders through, then the second page. Collected as a list
|
|
292
|
+
// rather than pushed one `if` at a time because the stylesheet below is
|
|
293
|
+
// derived from it — every one of these reads the theme tokens, and a
|
|
294
|
+
// second enumeration saying which ones do has been wrong every time it was
|
|
295
|
+
// written. Adding a page here cannot forget to bring the tokens with it.
|
|
296
|
+
const pages = [];
|
|
297
|
+
if (writesApp) {
|
|
298
|
+
pages.push({
|
|
272
299
|
file: 'src/App.tsrx',
|
|
273
300
|
summary:
|
|
274
301
|
mode === 'fullstack'
|
|
275
302
|
? 'create the route entry referenced by octane.config.ts'
|
|
276
303
|
: 'create the entry component',
|
|
277
|
-
|
|
304
|
+
body: appComponent(mode),
|
|
305
|
+
});
|
|
306
|
+
}
|
|
307
|
+
if (writesLayout) {
|
|
308
|
+
pages.push({
|
|
309
|
+
file: 'src/Layout.tsrx',
|
|
310
|
+
summary: 'create the frame both pages share',
|
|
311
|
+
body: layoutComponent,
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
if (writesCounter) {
|
|
315
|
+
pages.push({
|
|
316
|
+
file: 'src/Counter.tsrx',
|
|
317
|
+
summary: 'create the route entry for /counter',
|
|
318
|
+
body: counterComponent,
|
|
319
|
+
});
|
|
320
|
+
}
|
|
321
|
+
for (const page of pages) {
|
|
322
|
+
changes.push({
|
|
323
|
+
file: page.file,
|
|
324
|
+
summary: page.summary,
|
|
325
|
+
apply: writeFile(at(page.file), page.body),
|
|
278
326
|
});
|
|
279
327
|
}
|
|
328
|
+
|
|
329
|
+
// Not a page: it renders nothing and reads no tokens, so it is deliberately
|
|
330
|
+
// outside the list above.
|
|
331
|
+
if (scaffoldsRoutes && !existsSync(at('src/server/health.ts'))) {
|
|
332
|
+
changes.push({
|
|
333
|
+
file: 'src/server/health.ts',
|
|
334
|
+
summary: 'create the handler for GET /api/health',
|
|
335
|
+
apply: writeFile(at('src/server/health.ts'), healthRoute),
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
// The reset and theme tokens, which two separate things depend on: the
|
|
339
|
+
// shell links the file, and every component above reads the tokens in it.
|
|
340
|
+
// Either one alone is enough to need it, and they do not imply each other —
|
|
341
|
+
// this command writes pages without a shell when the project has its own
|
|
342
|
+
// `index.html`, and writes a shell without pages when the project has its
|
|
343
|
+
// own routing. Tying the stylesheet to just one of them leaves the other
|
|
344
|
+
// side broken: a page whose every colour, border and surface resolves to
|
|
345
|
+
// nothing, or a shell linking a file that was never created.
|
|
346
|
+
if ((writesShell || pages.length > 0) && !existsSync(at('src/styles.css'))) {
|
|
347
|
+
changes.push({
|
|
348
|
+
file: 'src/styles.css',
|
|
349
|
+
summary: 'create the reset and the theme tokens',
|
|
350
|
+
apply: writeFile(at('src/styles.css'), globalStyles),
|
|
351
|
+
});
|
|
352
|
+
// The shell this command writes carries the tag. When the project
|
|
353
|
+
// brought its own, splicing one into it is the same guesswork as
|
|
354
|
+
// rewriting their bundler config, so state the line instead.
|
|
355
|
+
if (!writesShell) {
|
|
356
|
+
manual.push(
|
|
357
|
+
'In index.html, add <link rel="stylesheet" href="/src/styles.css" />: the scaffolded pages read their theme tokens from it.',
|
|
358
|
+
);
|
|
359
|
+
}
|
|
360
|
+
}
|
|
280
361
|
}
|
|
281
362
|
|
|
282
363
|
const scripts = project.manifest.scripts ?? {};
|
|
@@ -83,31 +83,473 @@ export default defineConfig({
|
|
|
83
83
|
});
|
|
84
84
|
`;
|
|
85
85
|
|
|
86
|
-
/**
|
|
87
|
-
|
|
86
|
+
/**
|
|
87
|
+
* The routes, each naming its export so a typo fails loudly instead of
|
|
88
|
+
* rendering the wrong component. The health endpoint is a `ServerRoute`: it
|
|
89
|
+
* returns a `Response` rather than a component, which is the other half of the
|
|
90
|
+
* app layer and the piece that has no equivalent in the `spa` template.
|
|
91
|
+
*/
|
|
92
|
+
export const octaneConfig = `import { defineConfig, RenderRoute, ServerRoute } from "@octanejs/vite-plugin";
|
|
93
|
+
import { health } from "./src/server/health.ts";
|
|
88
94
|
|
|
89
95
|
export default defineConfig({
|
|
90
96
|
router: {
|
|
91
|
-
routes: [
|
|
97
|
+
routes: [
|
|
98
|
+
new RenderRoute({ path: "/", entry: ["App", "/src/App.tsrx"] }),
|
|
99
|
+
new RenderRoute({ path: "/counter", entry: ["Counter", "/src/Counter.tsrx"] }),
|
|
100
|
+
new ServerRoute({ path: "/api/health", handler: health }),
|
|
101
|
+
],
|
|
92
102
|
},
|
|
93
103
|
});
|
|
94
104
|
`;
|
|
95
105
|
|
|
96
|
-
/**
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
106
|
+
/**
|
|
107
|
+
* The Octane mark, as markup rather than an asset.
|
|
108
|
+
*
|
|
109
|
+
* Inline because the wordmark is two colors with different jobs: the glyph
|
|
110
|
+
* takes `currentColor` so it follows the page's light/dark text, and the flame
|
|
111
|
+
* keeps the brand red at every theme. An `<img>` can do neither, and a second
|
|
112
|
+
* file to keep in step with the stylesheet is not worth it for one logo.
|
|
113
|
+
*/
|
|
114
|
+
const LOGO = `<svg class="mark" viewBox="0 0 84 108" fill="none" aria-hidden="true">
|
|
115
|
+
<path
|
|
116
|
+
d="M71.0265 37.6009L65.8601 32.48C57.4266 24.1187 52.5193 13.6179 49.6691 1.55156C49.1892 -0.480713 46.2849 -0.534311 45.796 1.49572C43.135 12.5571 39.0884 21.441 31.9122 28.5539L29.1656 31.2763L32.1217 32.6051C34.5258 33.686 36.646 34.9098 38.604 36.3458C40.1609 37.487 41.4587 38.751 42.5898 40.0798L46.9068 35.8031L59.8758 48.6555C67.025 55.7439 67.025 67.2742 59.8758 74.3626L47.0622 87.0609C43.5969 90.4957 38.9915 92.385 34.0932 92.385C29.1949 92.385 24.5895 90.4935 21.1242 87.0609C13.975 79.9726 13.975 68.4422 21.1242 61.3539L26.4529 56.0722C27.6921 53.5084 29.6118 50.1138 31.1844 45.7858C32.4101 42.4136 31.8266 38.8649 29.4653 36.0733L27.1198 33.3041L9.97348 50.2992C-3.32449 63.4822 -3.32449 84.9304 9.97348 98.1133C16.6225 104.706 25.3578 108 34.0932 108C42.8286 108 51.564 104.704 58.213 98.1133L71.0265 85.415C84.3245 72.2321 84.3245 50.7838 71.0265 37.5986V37.6009Z"
|
|
117
|
+
fill="currentColor"
|
|
118
|
+
/>
|
|
119
|
+
<path
|
|
120
|
+
d="M53.682 51.4605C56.0095 56.1079 55.4237 66.6802 46.9654 65.3715C45.0232 65.07 43.2928 64.0941 42.7183 61.8943C42.3578 60.5097 43.3131 58.2697 43.7705 56.9387C46.481 49.0441 43.0923 42.4136 37.2657 38.1413C35.5736 36.9019 33.6314 35.7384 31.1913 34.6396C34.0595 38.0274 34.8684 42.3801 33.2732 46.6277C32.3224 48.9481 31.0606 51.3958 29.9543 53.6514C27.9671 57.7047 26.3178 62.504 27.575 69.1591C28.1879 72.4063 30.2743 75.0706 32.2503 76.9532C26.4259 77.6522 24.1886 73.811 23.0305 72.1986C21.7935 70.4767 20.9328 66.0973 22.7173 62.935C16.4491 69.1502 16.4491 79.2669 22.7173 85.4843C25.759 88.4969 29.7989 90.154 34.0933 90.154C38.3878 90.154 42.4276 88.4969 45.4694 85.4843L56.6562 74.3984C63.0663 67.8527 61.0543 58.7722 53.6798 51.4628L53.682 51.4605Z"
|
|
121
|
+
fill="#FF415A"
|
|
122
|
+
/>
|
|
123
|
+
</svg>`;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The documentation cards, matching the card treatment on octanejs.dev: the
|
|
127
|
+
* same radius, the same subtle surface, and the same accent-tinted hover rather
|
|
128
|
+
* than a plain border swap.
|
|
129
|
+
*/
|
|
130
|
+
const LINK_STYLES = ` .links {
|
|
131
|
+
display: grid;
|
|
132
|
+
grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
|
|
133
|
+
gap: 0.75rem;
|
|
134
|
+
width: 100%;
|
|
135
|
+
max-width: 42rem;
|
|
136
|
+
margin: 0;
|
|
137
|
+
padding: 0;
|
|
138
|
+
list-style: none;
|
|
139
|
+
}
|
|
140
|
+
.link {
|
|
141
|
+
display: block;
|
|
142
|
+
padding: 1rem;
|
|
143
|
+
border: 1px solid var(--border);
|
|
144
|
+
border-radius: 0.9rem;
|
|
145
|
+
background: var(--surface-subtle);
|
|
146
|
+
color: var(--text);
|
|
147
|
+
text-align: left;
|
|
148
|
+
text-decoration: none;
|
|
149
|
+
transition:
|
|
150
|
+
background 0.15s,
|
|
151
|
+
border-color 0.15s,
|
|
152
|
+
transform 0.15s;
|
|
153
|
+
}
|
|
154
|
+
.link:hover {
|
|
155
|
+
border-color: var(--card-hover-border);
|
|
156
|
+
background: var(--card-hover-bg);
|
|
157
|
+
transform: translateY(-1px);
|
|
158
|
+
}
|
|
159
|
+
.link-title {
|
|
160
|
+
display: block;
|
|
161
|
+
font-size: 0.95rem;
|
|
162
|
+
font-weight: 600;
|
|
163
|
+
}
|
|
164
|
+
.link-body {
|
|
165
|
+
display: block;
|
|
166
|
+
margin-top: 0.15rem;
|
|
167
|
+
color: var(--text-secondary);
|
|
168
|
+
font-size: 0.82rem;
|
|
169
|
+
line-height: 1.4;
|
|
170
|
+
}`;
|
|
171
|
+
|
|
172
|
+
/** Where the landing page sends people next. */
|
|
173
|
+
const DOC_LINKS = `const LINKS = [
|
|
174
|
+
{
|
|
175
|
+
href: "https://octanejs.dev/docs/quick-start",
|
|
176
|
+
title: "Quick start",
|
|
177
|
+
body: "Scaffold an app, mount a component, and the .tsrx essentials.",
|
|
178
|
+
},
|
|
179
|
+
{
|
|
180
|
+
href: "https://octanejs.dev/docs/core-apis",
|
|
181
|
+
title: "Core APIs",
|
|
182
|
+
body: "State, events, context, effects, async UI, and server rendering.",
|
|
183
|
+
},
|
|
184
|
+
{
|
|
185
|
+
href: "https://octanejs.dev/docs/tsrx-vs-tsx",
|
|
186
|
+
title: "TSRX vs TSX",
|
|
187
|
+
body: "What the .tsrx dialect adds on top of ordinary JSX.",
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
href: "https://octanejs.dev/docs/differences-from-react",
|
|
191
|
+
title: "Differences from React",
|
|
192
|
+
body: "The deliberate divergences. Everything else matching is the point.",
|
|
193
|
+
},
|
|
194
|
+
];`;
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* The landing page: the mark, the name, and the way into the documentation.
|
|
198
|
+
*
|
|
199
|
+
* `fullstack` renders it through the shared layout, so the two pages carry one
|
|
200
|
+
* header between them; `spa` has a single page and owns the frame itself.
|
|
201
|
+
*
|
|
202
|
+
* @param {keyof typeof MODES} mode
|
|
203
|
+
* @returns {string}
|
|
204
|
+
*/
|
|
205
|
+
export const appComponent = (mode) =>
|
|
206
|
+
mode === 'fullstack'
|
|
207
|
+
? `import { Layout } from "./Layout.tsrx";
|
|
208
|
+
|
|
209
|
+
${DOC_LINKS}
|
|
210
|
+
|
|
211
|
+
export function App() @{
|
|
212
|
+
<Layout>
|
|
213
|
+
<section class="hero">
|
|
214
|
+
${LOGO}
|
|
215
|
+
<h1 class="title">octane</h1>
|
|
216
|
+
<p class="lede">
|
|
217
|
+
React’s programming model, compiled. Edit src/App.tsrx and save — the page
|
|
218
|
+
updates without a reload.
|
|
219
|
+
</p>
|
|
220
|
+
<ul class="links">
|
|
221
|
+
@for (const link of LINKS; key link.href) {
|
|
222
|
+
<li>
|
|
223
|
+
<a class="link" href={link.href} target="_blank" rel="noreferrer">
|
|
224
|
+
<span class="link-title">{link.title as string}</span>
|
|
225
|
+
<span class="link-body">{link.body as string}</span>
|
|
226
|
+
</a>
|
|
227
|
+
</li>
|
|
228
|
+
}
|
|
229
|
+
</ul>
|
|
230
|
+
</section>
|
|
231
|
+
|
|
232
|
+
<style>
|
|
233
|
+
.hero {
|
|
234
|
+
display: flex;
|
|
235
|
+
flex-direction: column;
|
|
236
|
+
align-items: center;
|
|
237
|
+
gap: 1.25rem;
|
|
238
|
+
text-align: center;
|
|
239
|
+
}
|
|
240
|
+
.mark {
|
|
241
|
+
width: 5rem;
|
|
242
|
+
height: auto;
|
|
243
|
+
}
|
|
244
|
+
.title {
|
|
245
|
+
margin: 0;
|
|
246
|
+
font-size: 3rem;
|
|
247
|
+
line-height: 1.1;
|
|
248
|
+
letter-spacing: -0.03em;
|
|
249
|
+
}
|
|
250
|
+
.lede {
|
|
251
|
+
max-width: 34rem;
|
|
252
|
+
margin: 0;
|
|
253
|
+
color: var(--text-secondary);
|
|
254
|
+
font-size: 1.25rem;
|
|
255
|
+
}
|
|
256
|
+
${LINK_STYLES}
|
|
257
|
+
</style>
|
|
258
|
+
</Layout>
|
|
259
|
+
}
|
|
260
|
+
`
|
|
261
|
+
: `${DOC_LINKS}
|
|
262
|
+
|
|
263
|
+
export function App() @{
|
|
264
|
+
<main class="page">
|
|
265
|
+
${LOGO}
|
|
266
|
+
<h1 class="title">octane</h1>
|
|
267
|
+
<p class="lede">
|
|
268
|
+
React’s programming model, compiled. Edit src/App.tsrx and save — the page
|
|
269
|
+
updates without a reload.
|
|
270
|
+
</p>
|
|
271
|
+
<ul class="links">
|
|
272
|
+
@for (const link of LINKS; key link.href) {
|
|
273
|
+
<li>
|
|
274
|
+
<a class="link" href={link.href} target="_blank" rel="noreferrer">
|
|
275
|
+
<span class="link-title">{link.title as string}</span>
|
|
276
|
+
<span class="link-body">{link.body as string}</span>
|
|
277
|
+
</a>
|
|
278
|
+
</li>
|
|
279
|
+
}
|
|
280
|
+
</ul>
|
|
281
|
+
|
|
282
|
+
<style>
|
|
283
|
+
.page {
|
|
284
|
+
display: flex;
|
|
285
|
+
min-height: 100dvh;
|
|
286
|
+
flex-direction: column;
|
|
287
|
+
align-items: center;
|
|
288
|
+
justify-content: center;
|
|
289
|
+
gap: 1.25rem;
|
|
290
|
+
padding: 3rem 1.5rem;
|
|
291
|
+
text-align: center;
|
|
292
|
+
}
|
|
293
|
+
.mark {
|
|
294
|
+
width: 5rem;
|
|
295
|
+
height: auto;
|
|
296
|
+
}
|
|
297
|
+
.title {
|
|
298
|
+
margin: 0;
|
|
299
|
+
font-size: 3rem;
|
|
300
|
+
line-height: 1.1;
|
|
301
|
+
letter-spacing: -0.03em;
|
|
302
|
+
}
|
|
303
|
+
.lede {
|
|
304
|
+
max-width: 34rem;
|
|
305
|
+
margin: 0;
|
|
306
|
+
color: var(--text-secondary);
|
|
307
|
+
font-size: 1.25rem;
|
|
308
|
+
}
|
|
309
|
+
${LINK_STYLES}
|
|
310
|
+
</style>
|
|
101
311
|
</main>
|
|
102
312
|
}
|
|
103
313
|
`;
|
|
104
314
|
|
|
315
|
+
/**
|
|
316
|
+
* The frame the fullstack pages share. `children` is an ordinary prop, typed as
|
|
317
|
+
* `OctaneNode` — the renderable type, never React's `ReactNode`.
|
|
318
|
+
*/
|
|
319
|
+
export const layoutComponent = `import type { OctaneNode } from "octane";
|
|
320
|
+
|
|
321
|
+
interface LayoutProps {
|
|
322
|
+
children: OctaneNode;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
export function Layout(props: LayoutProps) @{
|
|
326
|
+
<div class="shell">
|
|
327
|
+
<nav class="nav">
|
|
328
|
+
<a class="nav-link" href="/">Home</a>
|
|
329
|
+
<a class="nav-link" href="/counter">Counter</a>
|
|
330
|
+
<a class={["nav-link", "nav-endpoint"]} href="/api/health">/api/health</a>
|
|
331
|
+
</nav>
|
|
332
|
+
<main class="main">{props.children}</main>
|
|
333
|
+
|
|
334
|
+
<style>
|
|
335
|
+
.shell {
|
|
336
|
+
display: flex;
|
|
337
|
+
min-height: 100dvh;
|
|
338
|
+
flex-direction: column;
|
|
339
|
+
}
|
|
340
|
+
/* The site's header treatment: sticky, translucent over the page, and
|
|
341
|
+
separated by a hairline rather than a block of colour. */
|
|
342
|
+
.nav {
|
|
343
|
+
position: sticky;
|
|
344
|
+
top: 0;
|
|
345
|
+
z-index: 1;
|
|
346
|
+
display: flex;
|
|
347
|
+
align-items: center;
|
|
348
|
+
gap: 1.25rem;
|
|
349
|
+
height: 60px;
|
|
350
|
+
padding: 0 1.5rem;
|
|
351
|
+
border-bottom: 1px solid var(--border);
|
|
352
|
+
background: var(--header-bg);
|
|
353
|
+
backdrop-filter: blur(8px);
|
|
354
|
+
}
|
|
355
|
+
.nav-link {
|
|
356
|
+
color: var(--text-secondary);
|
|
357
|
+
font-size: 0.9rem;
|
|
358
|
+
font-weight: 500;
|
|
359
|
+
}
|
|
360
|
+
.nav-link:hover {
|
|
361
|
+
color: var(--accent-text);
|
|
362
|
+
}
|
|
363
|
+
.nav-endpoint {
|
|
364
|
+
font-family: "SF Mono", SFMono-Regular, ui-monospace, Menlo, Consolas,
|
|
365
|
+
monospace;
|
|
366
|
+
font-size: 0.82rem;
|
|
367
|
+
}
|
|
368
|
+
.main {
|
|
369
|
+
display: flex;
|
|
370
|
+
flex: 1;
|
|
371
|
+
align-items: center;
|
|
372
|
+
justify-content: center;
|
|
373
|
+
padding: 3rem 1.5rem;
|
|
374
|
+
}
|
|
375
|
+
</style>
|
|
376
|
+
</div>
|
|
377
|
+
}
|
|
378
|
+
`;
|
|
379
|
+
|
|
380
|
+
/**
|
|
381
|
+
* The second page, and the reason the fullstack template has two.
|
|
382
|
+
*
|
|
383
|
+
* Its markup arrives from the server already rendered — view source and the
|
|
384
|
+
* count is in the HTML — and the button starts working once hydration reaches
|
|
385
|
+
* it. That is the whole difference from `spa` in one screen.
|
|
386
|
+
*/
|
|
387
|
+
export const counterComponent = `import { useState } from "octane";
|
|
388
|
+
import { Layout } from "./Layout.tsrx";
|
|
389
|
+
|
|
390
|
+
export function Counter() @{
|
|
391
|
+
const [count, setCount] = useState(0);
|
|
392
|
+
|
|
393
|
+
<Layout>
|
|
394
|
+
<section class="counter">
|
|
395
|
+
<h1 class="title">Server-rendered, then interactive</h1>
|
|
396
|
+
<p class="lede">
|
|
397
|
+
This page was rendered on the server and hydrated in the browser. View
|
|
398
|
+
source to see the markup arrive complete, then press the button.
|
|
399
|
+
</p>
|
|
400
|
+
<button class="button" onClick={() => setCount(count + 1)}>
|
|
401
|
+
{"Pressed " + count + " times"}
|
|
402
|
+
</button>
|
|
403
|
+
|
|
404
|
+
<style>
|
|
405
|
+
.counter {
|
|
406
|
+
display: flex;
|
|
407
|
+
flex-direction: column;
|
|
408
|
+
align-items: center;
|
|
409
|
+
gap: 1.25rem;
|
|
410
|
+
text-align: center;
|
|
411
|
+
}
|
|
412
|
+
.title {
|
|
413
|
+
margin: 0;
|
|
414
|
+
font-size: 2rem;
|
|
415
|
+
line-height: 1.1;
|
|
416
|
+
letter-spacing: -0.02em;
|
|
417
|
+
}
|
|
418
|
+
.lede {
|
|
419
|
+
max-width: 34rem;
|
|
420
|
+
margin: 0;
|
|
421
|
+
color: var(--text-secondary);
|
|
422
|
+
font-size: 1.25rem;
|
|
423
|
+
}
|
|
424
|
+
/* The site's primary action: an accent pill. */
|
|
425
|
+
.button {
|
|
426
|
+
padding: 0.7rem 1.4rem;
|
|
427
|
+
border: none;
|
|
428
|
+
border-radius: 9999px;
|
|
429
|
+
background: var(--accent);
|
|
430
|
+
color: var(--on-accent);
|
|
431
|
+
font: inherit;
|
|
432
|
+
font-weight: 600;
|
|
433
|
+
cursor: pointer;
|
|
434
|
+
transition: background 0.15s;
|
|
435
|
+
}
|
|
436
|
+
.button:hover {
|
|
437
|
+
background: var(--accent-hover);
|
|
438
|
+
}
|
|
439
|
+
</style>
|
|
440
|
+
</section>
|
|
441
|
+
</Layout>
|
|
442
|
+
}
|
|
443
|
+
`;
|
|
444
|
+
|
|
445
|
+
/**
|
|
446
|
+
* A server route: a plain function from the request context to a `Response`,
|
|
447
|
+
* with no component and no rendering involved.
|
|
448
|
+
*/
|
|
449
|
+
export const healthRoute = `/**
|
|
450
|
+
* Handles GET /api/health, declared in octane.config.ts.
|
|
451
|
+
*
|
|
452
|
+
* A ServerRoute returns a Response, so this never renders and never ships to
|
|
453
|
+
* the browser — it is the place for an API endpoint, a webhook, or a form
|
|
454
|
+
* action.
|
|
455
|
+
*/
|
|
456
|
+
export function health(): Response {
|
|
457
|
+
return Response.json({ status: "ok" });
|
|
458
|
+
}
|
|
459
|
+
`;
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* The reset and the theme tokens every component reads, taken from
|
|
463
|
+
* octanejs.dev so a scaffolded app and the documentation look like one thing.
|
|
464
|
+
*
|
|
465
|
+
* A stylesheet rather than a `<style>` in the shell. A component's own `<style>`
|
|
466
|
+
* is scoped — the compiler appends a scope hash to its selectors — so it can
|
|
467
|
+
* style that component's markup but never `body`, and these custom properties
|
|
468
|
+
* have to resolve for every component at once. Keeping them in a file the shell
|
|
469
|
+
* links, rather than inline in the shell, means `init` still has something to
|
|
470
|
+
* point at when the project brought its own `index.html` and kept it.
|
|
471
|
+
*
|
|
472
|
+
* Linked from the shell rather than imported by a component on purpose: a CSS
|
|
473
|
+
* import is injected by JavaScript in dev, so the tokens would arrive after the
|
|
474
|
+
* server-rendered markup that reads them and the first paint would flash.
|
|
475
|
+
*/
|
|
476
|
+
export const globalStyles = `:root {
|
|
477
|
+
--bg: #23272f;
|
|
478
|
+
--panel: #2b3138;
|
|
479
|
+
--text: #f4eee8;
|
|
480
|
+
--text-secondary: #99a1b3;
|
|
481
|
+
--accent: #ff415a;
|
|
482
|
+
--accent-hover: #ff5d72;
|
|
483
|
+
--accent-text: #ff5d72;
|
|
484
|
+
--on-accent: #16181d;
|
|
485
|
+
--border: rgba(255, 255, 255, 0.1);
|
|
486
|
+
--surface: rgba(255, 255, 255, 0.06);
|
|
487
|
+
--surface-subtle: rgba(255, 255, 255, 0.03);
|
|
488
|
+
--header-bg: rgba(35, 39, 47, 0.85);
|
|
489
|
+
/* Accent tints for the card hover, the same in both themes. */
|
|
490
|
+
--card-hover-border: rgba(255, 93, 114, 0.4);
|
|
491
|
+
--card-hover-bg: rgba(255, 65, 90, 0.07);
|
|
492
|
+
color-scheme: dark;
|
|
493
|
+
}
|
|
494
|
+
/* octanejs.dev defaults to dark and offers a toggle. A scaffold has no
|
|
495
|
+
toggle to offer, so the same palette follows the operating system
|
|
496
|
+
instead — and the values on both sides are the site's own. */
|
|
497
|
+
@media (prefers-color-scheme: light) {
|
|
498
|
+
:root {
|
|
499
|
+
--bg: #ffffff;
|
|
500
|
+
--panel: #f4f5f7;
|
|
501
|
+
--text: #1c2027;
|
|
502
|
+
--text-secondary: #5c6473;
|
|
503
|
+
--accent-hover: #c81e37;
|
|
504
|
+
--accent-text: #d81f38;
|
|
505
|
+
--on-accent: #ffffff;
|
|
506
|
+
--border: rgba(0, 0, 0, 0.12);
|
|
507
|
+
--surface: rgba(0, 0, 0, 0.04);
|
|
508
|
+
--surface-subtle: rgba(0, 0, 0, 0.025);
|
|
509
|
+
--header-bg: rgba(255, 255, 255, 0.85);
|
|
510
|
+
color-scheme: light;
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
* {
|
|
514
|
+
box-sizing: border-box;
|
|
515
|
+
}
|
|
516
|
+
body {
|
|
517
|
+
margin: 0;
|
|
518
|
+
background: var(--bg);
|
|
519
|
+
color: var(--text);
|
|
520
|
+
font-family:
|
|
521
|
+
Inter, system-ui, -apple-system, "Segoe UI", Roboto, Helvetica, Arial,
|
|
522
|
+
sans-serif;
|
|
523
|
+
font-size: 16px;
|
|
524
|
+
line-height: 1.6;
|
|
525
|
+
-webkit-font-smoothing: antialiased;
|
|
526
|
+
}
|
|
527
|
+
code,
|
|
528
|
+
pre,
|
|
529
|
+
kbd,
|
|
530
|
+
samp {
|
|
531
|
+
font-family: "SF Mono", SFMono-Regular, ui-monospace, Menlo, Consolas,
|
|
532
|
+
monospace;
|
|
533
|
+
}
|
|
534
|
+
a {
|
|
535
|
+
color: var(--accent-text);
|
|
536
|
+
text-decoration: none;
|
|
537
|
+
}
|
|
538
|
+
a:hover {
|
|
539
|
+
color: var(--accent-hover);
|
|
540
|
+
}
|
|
541
|
+
::selection {
|
|
542
|
+
background: rgba(255, 65, 90, 0.45);
|
|
543
|
+
}
|
|
544
|
+
`;
|
|
545
|
+
|
|
105
546
|
const SPA_INDEX_HTML = `<!doctype html>
|
|
106
547
|
<html lang="en">
|
|
107
548
|
<head>
|
|
108
549
|
<meta charset="utf-8" />
|
|
109
550
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
110
551
|
<title>Octane app</title>
|
|
552
|
+
<link rel="stylesheet" href="/src/styles.css" />
|
|
111
553
|
</head>
|
|
112
554
|
<body>
|
|
113
555
|
<div id="root"></div>
|
|
@@ -122,6 +564,7 @@ const SSR_INDEX_HTML = `<!doctype html>
|
|
|
122
564
|
<meta charset="utf-8" />
|
|
123
565
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
|
124
566
|
<title>Octane app</title>
|
|
567
|
+
<link rel="stylesheet" href="/src/styles.css" />
|
|
125
568
|
<!--ssr-head-->
|
|
126
569
|
</head>
|
|
127
570
|
<body>
|
|
@@ -226,7 +669,7 @@ export const tsconfig = `{
|
|
|
226
669
|
}
|
|
227
670
|
]
|
|
228
671
|
},
|
|
229
|
-
"include": ["src/**/*", "vite.config.ts"]
|
|
672
|
+
"include": ["src/**/*", "vite.config.ts", "octane.config.ts"]
|
|
230
673
|
}
|
|
231
674
|
`;
|
|
232
675
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"bindings": [
|
|
3
3
|
{
|
|
4
4
|
"name": "@octanejs/apollo-client",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.20",
|
|
6
6
|
"category": "AI, data, and routing",
|
|
7
7
|
"upstream": { "package": "@apollo/client", "version": "4.2.6" },
|
|
8
8
|
"surface": "Complete published client adapter surface: all 18 @apollo/client/react runtime exports and their Apollo 4.2.6 TypeScript declarations, framework-neutral root/testing exports, an Octane MockedProvider, and the Octane-native /react/ssr prerenderStatic entry.",
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
{
|
|
18
18
|
"name": "@octanejs/aria",
|
|
19
|
-
"version": "0.0.
|
|
19
|
+
"version": "0.0.19",
|
|
20
20
|
"category": "UI and interaction",
|
|
21
21
|
"upstream": { "package": "react-aria", "version": "3.50.0" },
|
|
22
22
|
"surface": "Phases 0-5 + the Tree/Table follow-up complete. Phases 0-1: the utils foundation, SSR utilities, the complete interactions area (usePress, useHover, focus/keyboard family, useLongPress, useMove, Pressable/PressResponder), the focus area (FocusScope with containment/restore/focus managers, FocusRing, useFocusRing, useHasTabbableChild), the i18n area (I18nProvider, locale/collator/formatter/filter hooks), form validation (useFormValidation + stately useFormValidationState), and the leaf hooks: useButton/useToggleButton(+Group), useLabel/useField, useCheckbox(+Group/+Item), useRadio/useRadioGroup, useSwitch, useTextField, useSearchField, useProgressBar, useMeter, useSeparator, useLink, useDisclosure, useToolbar, VisuallyHidden. Phase 2 adds the collections + selection tier: the stately collections engine (CollectionBuilder/Item/Section/useCollection) and selection core (Selection/SelectionManager/useMultipleSelectionState), the stately state hooks (useListState/useSingleSelectListState, useTreeState, useMenuTriggerState/useSubmenuTriggerState, useOverlayTriggerState, useSelectState, useComboBoxState, useTabListState, useNumberFieldState, useSliderState), the aria selection area (useSelectableCollection/-Item/-List, useTypeSelect, ListKeyboardDelegate, DOMLayoutDelegate), and the aria hooks useListBox/useOption/useListBoxSection, useMenu/useMenuItem/useMenuSection/useMenuTrigger/useSubmenuTrigger, useTab/useTabList/useTabPanel, useSlider/useSliderThumb, useNumberField, useGridList(+Item/+Section/+SelectionCheckbox), useTag/useTagGroup, useBreadcrumbs/useBreadcrumbItem — plus the matching react-stately state hooks under `@octanejs/aria/stately`. Phase 3 adds the overlays hooks tier: the stately `useTooltipTriggerState` and the whole aria overlays area (usePreventScroll, ariaHideOutside, DismissButton, PortalProvider, useOverlay, useOverlayTrigger, useOverlayPosition + calculatePosition, Overlay/useOverlayFocusContain, useModal/ModalProvider/OverlayProvider/OverlayContainer, useModalOverlay, usePopover), plus the consumers useDialog, useTooltip/useTooltipTrigger, useSelect/useHiddenSelect/HiddenSelect, and useComboBox. Differential-verified byte-identical against the real react-aria (interactions + button/toggle/checkbox/switch/radio/textfield/progress + tabs + listbox + select + combobox fixtures); dialog/tooltip/overlay focus-trap/dismiss/scroll-lock paths are covered by behavioral tests (the differential rig shares one document, so focus/portal/positioning aren't rig-driveable). Autocomplete (useAutocomplete/useSearchAutocomplete) is deferred — useComboBox does not depend on it in 3.50.0. Phase 4 adds the react-aria-components foundation under `@octanejs/aria/components`: the collections engine re-hosted on a detached real-DOM store (BaseCollection/CollectionBuilder/createLeafComponent/createBranchComponent/Hidden/useCachedChildren + Collection/Section), the RAC plumbing (Provider, useContextProps, slotted contexts, useRenderProps/composeRenderProps with data-* state attributes), and the non-collection components: Button, ToggleButton(+Group), Checkbox(+Group/Field/Button), Switch(+Field/Button), RadioGroup(+Radio/Field/Button), TextField, SearchField, NumberField, Form, Label/Input/TextArea/FieldError, Group, Toolbar, Separator, Header, Heading, Link, ProgressBar, Meter, Slider(+Output/Track/Thumb/Fill), Disclosure(+Group/Panel), DialogTrigger/Dialog, Modal/ModalOverlay, Popover, TooltipTrigger/Tooltip, OverlayArrow, Text, Keyboard, SelectionIndicator, SharedElementTransition. Phase-4 differentials drive the REAL components on both sides byte-identical (Button hover+mid-press, ToggleButton, Checkbox, TextField typing, Disclosure expand/collapse). Phase 5 adds the RAC collection components over that engine: Autocomplete (full — aria useAutocomplete + stately useAutocompleteState now ported), ListBox(+Item/Section/LoadMoreItem), Menu(+MenuTrigger/SubmenuTrigger/MenuItem/MenuSection), Select(+SelectValue), ComboBox(+ComboBoxValue), Tabs(+TabList/Tab/TabPanels/TabPanel), TagGroup(+TagList/Tag), GridList(+Item/Section/Header/LoadMoreItem), Breadcrumbs(+Breadcrumb), and the DragAndDrop context layer (DropIndicator/contexts/DragAndDropHooks type; components' dnd branches are inert — the dnd engine and useDragAndDrop() itself arrive in a later phase, the stub throws). Phase-5 differentials drive the REAL react-aria-components byte-identical (ListBox selection + keyed reverse, Tabs switch, TagGroup multi-select, GridList row selection, Breadcrumbs, ComboBox typing); Menu/Select open-state (portal'd) carries behavioral coverage incl. keyboard-driven submenus. The Tree/Table follow-up adds the remaining collection verticals: stately grid (GridCollection/useGridState) + the full stately table area (TableCollection/useTableState/column-resize state/UNSTABLE_useTreeGridState), the aria tree hooks (useTree/useTreeItem) and the full aria table hook area (useTable family, TableKeyboardDelegate, useTableColumnResize, grid hooks it rides on), RAC Tree(+TreeItem/TreeItemContent/TreeSection/TreeHeader/TreeLoadMoreItem) and RAC Table(+TableHeader/TableBody/Column/Row/Cell/ColumnResizer/ResizableTableContainer/TableFooter/TableLoadMoreItem). Tree structure states and the interactive Table (sort cycling, row selection) are differential-verified byte-identical vs the real react-aria-components; chevron-driven Tree interaction carries behavioral coverage (the rig's virtual clicks cannot faithfully reproduce the focus-effect interplay on the React side). TableLayout lands with the Virtualizer; date/color families and the drag-and-drop engine are not started — see the migration plan.",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
},
|
|
32
32
|
{
|
|
33
33
|
"name": "@octanejs/base-ui",
|
|
34
|
-
"version": "0.1.
|
|
34
|
+
"version": "0.1.23",
|
|
35
35
|
"category": "UI and interaction",
|
|
36
36
|
"upstream": { "package": "@base-ui/react", "version": "1.6.0" },
|
|
37
37
|
"surface": "Alpha, in progress: 34 of 43 upstream subpaths. The foundation, overlay infrastructure, hover/focus interaction layer, list-navigation/typeahead layer and popup viewport have landed, along with Dialog, AlertDialog, Popover, Tooltip, PreviewCard, the full form-control set, and the standalone Button/DirectionProvider/CSPProvider/useMediaQuery utilities — ported at full fidelity and differential-verified against the real `@base-ui/react`. Menu is COMPLETE at all 20 upstream parts — Root/Trigger/Portal/Positioner/Popup, the full item family (Item, LinkItem, CheckboxItem, RadioGroup/RadioItem and their indicators, Group/GroupLabel, Separator), Arrow, Backdrop, Viewport, and SubmenuRoot/SubmenuTrigger. Menubar and ContextMenu have landed too, so the whole menu family is complete. Toast is complete at all 11 parts, including the imperative toast manager, auto-dismiss timers with hover/focus pausing, and promise toasts; swipe-to-dismiss is covered too. Collapsible is complete at all 3 upstream parts (Root/Trigger/Panel), including upstream's <Activity> animation-resume suppression — octane's Activity has React's contract (hidden subtrees keep state and DOM while effects tear down and re-fire on reveal), so that path is ported rather than skipped and is the first component carrying a port of Base UI's OWN test file, under tests/upstream/ — 16 of its 35 upstream cases, being every case that does not require real layout measurement, CSS transition/keyframe sequencing, React.Activity, or find-in-page `beforematch`. Accordion is complete at all 5 upstream parts (Root/Item/Header/Trigger/Panel), built directly on Collapsible — each Item runs its own useCollapsibleRoot and provides CollapsibleRootContext, exactly as upstream does. It carries 18 tests: 15 ported from upstream's own suite plus 3 written to close a real gap — upstream only exercises single-vs-multiple toggling inside jsdom-skipped blocks, so removing the single-mode collapse branch passed every ported case.",
|
|
@@ -47,7 +47,7 @@
|
|
|
47
47
|
},
|
|
48
48
|
{
|
|
49
49
|
"name": "@octanejs/cmdk",
|
|
50
|
-
"version": "0.1.
|
|
50
|
+
"version": "0.1.8",
|
|
51
51
|
"category": "UI and interaction",
|
|
52
52
|
"upstream": { "package": "cmdk", "version": "1.1.1" },
|
|
53
53
|
"surface": "Complete against the published `cmdk@1.1.1` public surface: `Command` (the root itself) and the `CommandRoot` named export, `Command.Input`, `Command.List`, `Command.Item`, `Command.Group`, `Command.Separator`, `Command.Dialog`, `Command.Empty`, `Command.Loading`, the flat `CommandX` aliases, `useCommandState`, and `defaultFilter` — with the DOM-authoritative store and item/group registration, `useValue` text-content inference, `onInput`-driven search, score filtering plus item and group DOM sorting, keyboard navigation (arrows/Home/End/vim/Enter), controlled `value`/`onValueChange`/`loop`/`shouldFilter`/custom `filter`/`forceMount`, the `--cmdk-list-height` ResizeObserver, and a Radix-backed `Command.Dialog`. `asChild` is the one unsupported prop (see divergences).",
|
|
@@ -78,7 +78,7 @@
|
|
|
78
78
|
},
|
|
79
79
|
{
|
|
80
80
|
"name": "@octanejs/devtools",
|
|
81
|
-
"version": "0.0.
|
|
81
|
+
"version": "0.0.13",
|
|
82
82
|
"category": "Styling, tests, and devtools",
|
|
83
83
|
"upstream": { "package": "octane", "version": "workspace" },
|
|
84
84
|
"surface": "Octane-native DevTools plugin (not an upstream port): renders live runtime diagnostics into a TanStack Devtools host via @tanstack/devtools-event-client. P1 ships the Components tree + state inspector.",
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
},
|
|
89
89
|
{
|
|
90
90
|
"name": "@octanejs/dexie",
|
|
91
|
-
"version": "0.1.
|
|
91
|
+
"version": "0.1.18",
|
|
92
92
|
"category": "AI, data, and routing",
|
|
93
93
|
"upstream": { "package": "dexie-react-hooks", "version": "4.4.0" },
|
|
94
94
|
"surface": "Port of the public dexie-react-hooks surface: useObservable, useLiveQuery, useSuspendingObservable, useSuspendingLiveQuery, usePermissions, and useDocument, with Dexie's framework-neutral API re-exported from the package root.",
|
|
@@ -102,7 +102,7 @@
|
|
|
102
102
|
},
|
|
103
103
|
{
|
|
104
104
|
"name": "@octanejs/dnd-kit",
|
|
105
|
-
"version": "0.1.
|
|
105
|
+
"version": "0.1.20",
|
|
106
106
|
"category": "UI and interaction",
|
|
107
107
|
"upstream": { "package": "@dnd-kit/react", "version": "0.5.0" },
|
|
108
108
|
"surface": "Complete modern dnd-kit React-adapter surface: DragDropProvider, DragOverlay, useDraggable/useDroppable, manager/monitor/operation hooks, PointerSensor/KeyboardSensor re-exports, the public signal-hook utilities, useSortable, and all four upstream entry points.",
|
|
@@ -113,9 +113,27 @@
|
|
|
113
113
|
"ssr": "Static SSR and hydration are covered; DOM plugins initialize only after client refs register.",
|
|
114
114
|
"verified": "2026-07-15"
|
|
115
115
|
},
|
|
116
|
+
{
|
|
117
|
+
"name": "@octanejs/electron",
|
|
118
|
+
"version": "0.0.3",
|
|
119
|
+
"category": "Desktop",
|
|
120
|
+
"upstream": { "package": "electron", "version": "43.2.0" },
|
|
121
|
+
"surface": "Process-split Electron bindings: ./main registers ipcMain handlers, ./main/native re-exports main-only Electron APIs (Menu, Tray, session, protocol, BrowserWindow, …), ./preload exposes Electron IPC and desktop helpers via contextBridge, and the renderer entry provides Octane hooks (useInvoke, useInvokeState, useIpcEvent, useNativeTheme, useWindowState) plus promise helpers for app/window/dialog/shell/clipboard/screen. Menu/Tray/session/protocol stay intentional main-only under contextIsolation.",
|
|
122
|
+
"divergences": [
|
|
123
|
+
"There is no React binding upstream; Electron is framework-agnostic, so this package mirrors the React Electron process layout rather than porting a React library.",
|
|
124
|
+
"Renderer code uses window.__OCTANE_ELECTRON__ because contextIsolation forbids importing electron in the page.",
|
|
125
|
+
"Menu, Tray, session, and protocol are re-exported from @octanejs/electron/main/native for main-process consumers and are intentionally not bridged into the renderer.",
|
|
126
|
+
"Hook call-site slots are forwarded through Octane's compiler binding ABI.",
|
|
127
|
+
"useInvoke integrates with Octane's use() rather than React's use().",
|
|
128
|
+
"useInvokeState returns to pending on refetch and does not implement stale-while-revalidate.",
|
|
129
|
+
"Built-in desktop helpers use octane:* IPC channels; apps may allowlist additional channels in preload."
|
|
130
|
+
],
|
|
131
|
+
"ssr": "Server rendering performs no IPC. useInvokeState renders pending and issues the command on the client after hydration; useIpcEvent and reactive desktop hooks subscribe only on the client. useInvoke without a host rejects with ElectronUnavailableError.",
|
|
132
|
+
"verified": "2026-08-02"
|
|
133
|
+
},
|
|
116
134
|
{
|
|
117
135
|
"name": "@octanejs/floating-ui",
|
|
118
|
-
"version": "0.1.
|
|
136
|
+
"version": "0.1.24",
|
|
119
137
|
"category": "UI and interaction",
|
|
120
138
|
"upstream": { "package": "@floating-ui/react", "version": "0.27.19" },
|
|
121
139
|
"surface": "Positioning (`useFloating`, ref-aware `arrow`, the `@floating-ui/dom` middleware re-exports, the floating tree), the full interaction-hook set (`useInteractions`, `useHover` + `safePolygon`, `useClick`, `useFocus`, `useDismiss`, `useRole`, `useClientPoint`, `useListNavigation`, `useTypeahead`), the component layer (`FloatingPortal`, `FloatingOverlay`, `FloatingFocusManager`, `FloatingArrow`, `FloatingList`, `Composite`), and transitions + `FloatingDelayGroup`.",
|
|
@@ -125,20 +143,20 @@
|
|
|
125
143
|
},
|
|
126
144
|
{
|
|
127
145
|
"name": "@octanejs/hook-form",
|
|
128
|
-
"version": "0.1.
|
|
146
|
+
"version": "0.1.22",
|
|
129
147
|
"category": "Forms and content",
|
|
130
148
|
"upstream": { "package": "react-hook-form", "version": "7.81.0" },
|
|
131
|
-
"surface": "Complete port of react-hook-form 7.81.0 (
|
|
149
|
+
"surface": "Complete port of react-hook-form 7.81.0 (tag commit 46b217e034dd92f7aa3cb3a478815556b416b299). The automated parity check runs all 1,193 original tests against the pinned React package as a pristine baseline; the Octane port separately runs byte-locked, unfiltered DOM and server suites with exact collected/executed inventories containing 1,187 entries representing 1,178 unique file/full-name identities. The nine duplicate entries are repeated titles within the DOM inventory; the server inventory is disjoint. Coverage includes `useForm`, `useController`, `useFieldArray`, `useFormState`, `useWatch`, `useFormContext`/`FormProvider`, schema resolvers, and all validation modes.",
|
|
132
150
|
"divergences": [
|
|
133
151
|
"`register()` returns `onInput` (octane's native per-keystroke event) instead of React's synthetic `onChange`; mode names and `register` option keys keep the upstream spelling.",
|
|
134
|
-
"
|
|
152
|
+
"The structured parity ledger records native no-op input delivery, microtask batching, duplicate resolver notification, async act flush, reset render-count, and eager `Object.is` bailout differences with executable case identities, consumer impact, and migration guidance; the suite contains no skipped or expected-failure cases."
|
|
135
153
|
],
|
|
136
154
|
"ssr": "Supported and tested — the upstream `*.server.test.tsx` suite runs via `octane/server` with byte-identical markup.",
|
|
137
|
-
"verified": "2026-
|
|
155
|
+
"verified": "2026-08-01"
|
|
138
156
|
},
|
|
139
157
|
{
|
|
140
158
|
"name": "@octanejs/i18next",
|
|
141
|
-
"version": "0.1.
|
|
159
|
+
"version": "0.1.20",
|
|
142
160
|
"category": "Forms and content",
|
|
143
161
|
"upstream": { "package": "react-i18next", "version": "17.0.9" },
|
|
144
162
|
"surface": "Complete runtime port of react-i18next 17.0.9: useTranslation, I18nextProvider/context, Trans/TransWithoutContext, IcuTrans/IcuTransWithoutContext, Translation, the withTranslation/withSSR HOCs, useSSR, namespace reporting, initialization/default helpers, and the root ICU helper exports over the unchanged i18next core.",
|
|
@@ -153,7 +171,7 @@
|
|
|
153
171
|
},
|
|
154
172
|
{
|
|
155
173
|
"name": "@octanejs/jotai",
|
|
156
|
-
"version": "0.1.
|
|
174
|
+
"version": "0.1.22",
|
|
157
175
|
"category": "Shared state",
|
|
158
176
|
"upstream": { "package": "jotai", "version": "2.20.2" },
|
|
159
177
|
"surface": "Complete 1:1 port: the framework-agnostic vanilla core (`jotai/vanilla`, `/vanilla/utils`, `/vanilla/internals`) is reused verbatim; the React layer (`Provider`, `useStore`, `useAtom`, `useAtomValue`, `useSetAtom`) and `react/utils` (`useResetAtom`, `useReducerAtom`, `useAtomCallback`, `useHydrateAtoms`) are ported onto octane hooks, preserving upstream's useReducer force-update + effect-subscription implementation, async atoms via octane's `use()`.",
|
|
@@ -163,7 +181,7 @@
|
|
|
163
181
|
},
|
|
164
182
|
{
|
|
165
183
|
"name": "@octanejs/lexical",
|
|
166
|
-
"version": "0.1.
|
|
184
|
+
"version": "0.1.24",
|
|
167
185
|
"category": "Forms and content",
|
|
168
186
|
"upstream": { "package": "@lexical/react", "version": "0.46.0" },
|
|
169
187
|
"surface": "35 of 39 `@lexical/react` modules ported: composer + contexts, the editable surface, plain/rich text, and the full plugin/menu set (history, lists + check-list, links, tables, markdown shortcuts, the typeahead/node-menu/context-menu family, draggable-block, character-limit, …) plus the `useLexical*` hooks.",
|
|
@@ -176,7 +194,7 @@
|
|
|
176
194
|
},
|
|
177
195
|
{
|
|
178
196
|
"name": "@octanejs/lucide",
|
|
179
|
-
"version": "0.1.
|
|
197
|
+
"version": "0.1.20",
|
|
180
198
|
"category": "UI and interaction",
|
|
181
199
|
"upstream": { "package": "lucide-react", "version": "1.24.0" },
|
|
182
200
|
"surface": "Complete against the published `lucide-react@1.24.0` runtime surface: every canonical icon and alias, the `icons` namespace, `Icon`, `createLucideIcon`, `LucideProvider`, `useLucideContext`, `DynamicIcon`, `iconNames`, `dynamicIconImports`, and per-icon subpath imports.",
|
|
@@ -189,7 +207,7 @@
|
|
|
189
207
|
},
|
|
190
208
|
{
|
|
191
209
|
"name": "@octanejs/mantine-hooks",
|
|
192
|
-
"version": "0.1.
|
|
210
|
+
"version": "0.1.6",
|
|
193
211
|
"category": "UI and interaction",
|
|
194
212
|
"upstream": { "package": "@mantine/hooks", "version": "9.5.0" },
|
|
195
213
|
"surface": "Complete @mantine/hooks 9.5.0 runtime export surface: state, timing, storage, viewport, input, focus, pointer, observer, hotkey, scrolling, collapse, drag, splitter, mask, and utility hooks.",
|
|
@@ -203,7 +221,7 @@
|
|
|
203
221
|
},
|
|
204
222
|
{
|
|
205
223
|
"name": "@octanejs/mdx",
|
|
206
|
-
"version": "0.1.
|
|
224
|
+
"version": "0.1.22",
|
|
207
225
|
"category": "Forms and content",
|
|
208
226
|
"upstream": { "package": "@mdx-js/mdx", "version": "3.1.1" },
|
|
209
227
|
"surface": "The full compile-don't-interpret pipeline: `.mdx`/`.md` → `@mdx-js/mdx` (reused verbatim) → octane compiler, via the `octaneMdx()` Vite plugin plus the `./compile` and `./server` entries; compiler warnings propagate through direct and Vite compile surfaces with authored `.mdx` ranges; `@mdx-js/react`'s provider layer (`MDXProvider`/`useMDXComponents`) is ported onto octane context. The octane website runs on it.",
|
|
@@ -215,7 +233,7 @@
|
|
|
215
233
|
},
|
|
216
234
|
{
|
|
217
235
|
"name": "@octanejs/mobx",
|
|
218
|
-
"version": "0.1.
|
|
236
|
+
"version": "0.1.6",
|
|
219
237
|
"category": "Shared state",
|
|
220
238
|
"upstream": { "package": "mobx-react-lite", "version": "4.1.1", "core": "mobx@6.16.1" },
|
|
221
239
|
"surface": "The framework-independent MobX core is re-exported verbatim. The function-component binding includes observer, Observer, useObserver, useLocalObservable, enableStaticRendering, isUsingStaticRendering, and the deprecated useStaticRendering alias.",
|
|
@@ -229,7 +247,7 @@
|
|
|
229
247
|
},
|
|
230
248
|
{
|
|
231
249
|
"name": "@octanejs/motion",
|
|
232
|
-
"version": "0.1.
|
|
250
|
+
"version": "0.1.24",
|
|
233
251
|
"category": "UI and interaction",
|
|
234
252
|
"upstream": { "package": "motion", "version": "12.42.2" },
|
|
235
253
|
"surface": "Core surface: `motion.<tag>` (animate, gestures, variants with propagation/stagger, drag, layout basics), `AnimatePresence`, `MotionConfig`, and the motion-value hooks (`useMotionValue`, `useScroll`, `useTransform`, `useSpring`, `useAnimate`, `useMotionValueEvent`); motion-dom's animation engine and gesture primitives are reused verbatim.",
|
|
@@ -242,7 +260,7 @@
|
|
|
242
260
|
},
|
|
243
261
|
{
|
|
244
262
|
"name": "@octanejs/nuqs",
|
|
245
|
-
"version": "0.1.
|
|
263
|
+
"version": "0.1.14",
|
|
246
264
|
"category": "AI, data, and routing",
|
|
247
265
|
"upstream": { "package": "nuqs", "version": "2.9.1" },
|
|
248
266
|
"surface": "Full vendored port: the framework-agnostic core (`parsers`/`parseAs*`/`createParser`, `createSerializer`, `createLoader`, `createStandardSchemaV1`, the throttle/debounce update queues, sync emitter and URL encoding) is vendored verbatim from nuqs 2.9.1; the React layer (`useQueryState`, `useQueryStates`, the `useSyncExternalStores` helper and the adapter context) is ported onto octane's hooks — same `useState`/`useEffect`/`useSyncExternalStore` implementation shape as upstream, so re-render and URL-reconciliation behaviour matches nuqs on React. Adapters ported: `@octanejs/nuqs/adapters/react` (`NuqsAdapter`, `enableHistorySync`), `/adapters/custom` (`unstable_createAdapterProvider`), `/adapters/testing` (`NuqsTestingAdapter`, `withNuqsTestingAdapter`). Server surface (`@octanejs/nuqs/server`) exposes `createLoader`/`createSerializer`/parsers/`createStandardSchemaV1`.",
|
|
@@ -257,7 +275,7 @@
|
|
|
257
275
|
},
|
|
258
276
|
{
|
|
259
277
|
"name": "@octanejs/phosphor-icons",
|
|
260
|
-
"version": "0.0.
|
|
278
|
+
"version": "0.0.5",
|
|
261
279
|
"category": "UI and interaction",
|
|
262
280
|
"upstream": { "package": "@phosphor-icons/react", "version": "2.1.10" },
|
|
263
281
|
"surface": "All 1,512 canonical icons from @phosphor-icons/core@2.1.1, including the upstream deprecated Icon-suffixed aliases, six weights, IconContext, IconBase, root exports, and per-icon imports.",
|
|
@@ -271,7 +289,7 @@
|
|
|
271
289
|
},
|
|
272
290
|
{
|
|
273
291
|
"name": "@octanejs/radix",
|
|
274
|
-
"version": "0.1.
|
|
292
|
+
"version": "0.1.24",
|
|
275
293
|
"category": "UI and interaction",
|
|
276
294
|
"upstream": { "package": "radix-ui", "version": "1.6.4" },
|
|
277
295
|
"surface": "Complete against the unified `radix-ui@1.6.4` component surface — all primitives (incl. Dialog, the Menu/DropdownMenu/ContextMenu family, Popover, Tooltip, Select, NavigationMenu, Toast, Menubar, Slider, the form controls, and OneTimePasswordField/PasswordToggleField) plus the composition/state/overlay foundations — verified by a differential suite (same fixtures through octane and the real radix-ui, byte-identical DOM).",
|
|
@@ -284,7 +302,7 @@
|
|
|
284
302
|
},
|
|
285
303
|
{
|
|
286
304
|
"name": "@octanejs/rainbowkit",
|
|
287
|
-
"version": "0.0.
|
|
305
|
+
"version": "0.0.5",
|
|
288
306
|
"category": "Web3",
|
|
289
307
|
"upstream": { "package": "@rainbow-me/rainbowkit", "version": "2.2.11" },
|
|
290
308
|
"surface": "Octane-native RainbowKitProvider, ConnectButton and ConnectButton.Custom, WalletButton, connect/account/chain modal hooks, connector selection, account/chain actions, native accessible dialogs, and light/dark/midnight themes.",
|
|
@@ -300,7 +318,7 @@
|
|
|
300
318
|
},
|
|
301
319
|
{
|
|
302
320
|
"name": "@octanejs/react-error-boundary",
|
|
303
|
-
"version": "0.1.
|
|
321
|
+
"version": "0.1.6",
|
|
304
322
|
"category": "UI and interaction",
|
|
305
323
|
"upstream": { "package": "react-error-boundary", "version": "6.1.2" },
|
|
306
324
|
"surface": "Complete against the published react-error-boundary 6.1.2 function/type surface adapted to Octane: ErrorBoundary, ErrorBoundaryContext, getErrorMessage, fallback variants, onError/onReset callbacks, resetKeys, useErrorBoundary (including error), withErrorBoundary, OnErrorCallback, and UseErrorBoundaryApi.",
|
|
@@ -314,7 +332,7 @@
|
|
|
314
332
|
},
|
|
315
333
|
{
|
|
316
334
|
"name": "@octanejs/recharts",
|
|
317
|
-
"version": "0.1.
|
|
335
|
+
"version": "0.1.22",
|
|
318
336
|
"category": "Data-heavy screens",
|
|
319
337
|
"upstream": { "package": "recharts", "version": "3.9.2" },
|
|
320
338
|
"surface": "Broad runtime support across cartesian, polar, hierarchical, tooltip, legend, responsive-container, shape, and chart-state surfaces. `Brush` and `Treemap` remain intentionally unsupported.",
|
|
@@ -326,7 +344,7 @@
|
|
|
326
344
|
},
|
|
327
345
|
{
|
|
328
346
|
"name": "@octanejs/redux",
|
|
329
|
-
"version": "0.1.
|
|
347
|
+
"version": "0.1.22",
|
|
330
348
|
"category": "Shared state",
|
|
331
349
|
"upstream": { "package": "react-redux", "version": "9.3.0" },
|
|
332
350
|
"surface": "The hooks + `Provider` surface of react-redux 9.3.0 (`useSelector`, `useDispatch`, `useStore`, and the custom-context factory variants) on octane's `useSyncExternalStore`; works with any Redux 5 / Redux Toolkit store. Export parity is pinned by test.",
|
|
@@ -339,7 +357,7 @@
|
|
|
339
357
|
},
|
|
340
358
|
{
|
|
341
359
|
"name": "@octanejs/redux-toolkit",
|
|
342
|
-
"version": "0.1.
|
|
360
|
+
"version": "0.1.20",
|
|
343
361
|
"category": "Shared state",
|
|
344
362
|
"upstream": { "package": "@reduxjs/toolkit", "version": "2.12.0" },
|
|
345
363
|
"surface": "Complete four-entry-point port: the framework-agnostic Toolkit and RTK Query core are re-exported verbatim; `/query/react` provides generated query, lazy-query, mutation, infinite-query, prefetch hooks and `ApiProvider`; `/react` provides the dynamic-middleware dispatch-hook integration.",
|
|
@@ -352,7 +370,7 @@
|
|
|
352
370
|
},
|
|
353
371
|
{
|
|
354
372
|
"name": "@octanejs/remix-router",
|
|
355
|
-
"version": "0.1.
|
|
373
|
+
"version": "0.1.21",
|
|
356
374
|
"category": "AI, data, and routing",
|
|
357
375
|
"upstream": { "package": "react-router", "version": "8.2.0" },
|
|
358
376
|
"surface": "COMPLETE port (all phases shipped — full export parity, EXPECTED_MISSING is empty): the framework-agnostic router core (lib/router/* + framework-free helpers, ~12k lines) is vendored byte-close and validated by 161 ported upstream router tests plus four focused v8.2 regression pins; the data-mode React layer (createMemoryRouter, RouterProvider incl. the /dom flushSync variant, Outlet, Await, RenderErrorBoundary/errorElement, Link + useLinkClickHandler, and the full read-hook family) and the declarative layer (MemoryRouter, Routes/Route in BOTH children forms — descriptor children walked upstream-style, .tsrx block children via a registration collector — Navigate, createRoutesFromChildren/Elements, the UNSAFE_With*Props wrappers) and the DOM layer (createBrowserRouter/createHashRouter with __staticRouterHydrationData parsing, BrowserRouter/HashRouter/unstable_HistoryRouter, Link + NavLink incl. the isActive/isPending render props, useLinkClickHandler, useSearchParams) and the mutation layer (Form on octane's native delegated submit event, useSubmit incl. JSON encTypes, useFormAction with ?index resolution, useFetcher/useFetchers incl. fetcher.Form/load/submit/reset and shared keys), the guard/scroll layer (useBlocker, unstable_usePrompt, ScrollRestoration/UNSAFE_useScrollRestoration, useBeforeUnload, useViewTransitionState, unstable_useRoute/unstable_useRouterState), static SSR (StaticRouter, StaticRouterProvider, createStaticHandler/createStaticRouter rendering through octane/server — markup byte-identical to react-dom/server after marker stripping, hydration payload identical), and the vendored cookie/session server runtime (createCookie/createSession/createCookieSessionStorage/createMemorySessionStorage) are transcribed onto octane and differential-verified against real react-router. Framework-mode + RSC names (Meta/Links/Scripts, createRequestHandler, UNSAFE_ internals) exist as THROWING STUBS so parity is honest.",
|
|
@@ -368,7 +386,7 @@
|
|
|
368
386
|
},
|
|
369
387
|
{
|
|
370
388
|
"name": "@octanejs/rxjs",
|
|
371
|
-
"version": "0.1.
|
|
389
|
+
"version": "0.1.4",
|
|
372
390
|
"category": "Shared state",
|
|
373
391
|
"upstream": {
|
|
374
392
|
"package": "@react-rxjs/core + @react-rxjs/utils",
|
|
@@ -384,13 +402,13 @@
|
|
|
384
402
|
},
|
|
385
403
|
{
|
|
386
404
|
"name": "@octanejs/shadcn",
|
|
387
|
-
"version": "0.0.
|
|
405
|
+
"version": "0.0.10",
|
|
388
406
|
"category": "UI and interaction",
|
|
389
407
|
"upstream": {
|
|
390
408
|
"package": "shadcn-ui/ui (radix base)",
|
|
391
409
|
"version": "4baadbc6517070ae8f8feb2c97037adc2b305544 + shadcn@4.14.1"
|
|
392
410
|
},
|
|
393
|
-
"surface": "Two primitive bases. RADIX base (default, reached at the bare subpaths): tiers 1-2 complete plus the first tier-3 composites — 40 component families. REACT ARIA base (@octanejs/shadcn/react-aria/<Family>): 33 families over @octanejs/aria/components, taken from upstream's aria-nova style and verified class-string identical to it; not ported are select and sonner (need input-group and next-themes), while hover-card, menubar and navigation-menu do not exist in upstream's aria base. Sources live at src/bases/<base>/ui/ with lib/hooks/styles shared. DISTRIBUTION is registry-first with per-family subpath exports; the monolithic root entry is removed. STYLING FLAVOR: the radix base is mid-migration from the pinned cn-* semantic-hook system to the default-Tailwind utilities-inlined flavor (user-directed); the react-aria base is uniformly the utilities-inlined aria-nova output.
|
|
411
|
+
"surface": "Two primitive bases. RADIX base (default, reached at the bare subpaths): tiers 1-2 complete plus the first tier-3 composites — 40 component families. REACT ARIA base (@octanejs/shadcn/react-aria/<Family>): 33 families over @octanejs/aria/components, taken from upstream's aria-nova style and verified class-string identical to it; not ported are select and sonner (need input-group and next-themes), while hover-card, menubar and navigation-menu do not exist in upstream's aria base. Sources live at src/bases/<base>/ui/ with lib/hooks/styles shared. DISTRIBUTION is registry-first with per-family subpath exports; the monolithic root entry is removed. STYLING FLAVOR: the radix base is mid-migration from the pinned cn-* semantic-hook system to the default-Tailwind utilities-inlined flavor (user-directed); the react-aria base is uniformly the utilities-inlined aria-nova output. THE REGISTRY NOW COVERS ALL THREE BASES, using shadcn's own mechanism rather than a namespace: base and visual style compose into components.json's single `style` field, which the CLI substitutes into the registry URL (`{style}` and `{name}` are the only placeholders shadcn@4.14.1 substitutes, and it never parses the style string). Emitted as registry/styles/<style>/<name>.json for base-nova (default, @octanejs/base-ui), radix-nova and aria-nova, plus an un-styled copy of the default so a URL without {style} still resolves. Base-agnostic items (utils/types/theme/use-mobile) are emitted into every style tree because registryDependencies resolve through the same templated URL. `registry:serve` serves it locally — the port the playground's components.json always referenced but which nothing previously served. Verified end to end by installing with the real shadcn CLI: each style yields its own primitive. BASE UI base (@octanejs/shadcn/base-ui/<Family>): STARTED, 21 of 44 families and PARTIAL. `accordion` is transcribed from upstream and runs on the @octanejs/base-ui accordion primitive — the first primitive-backed family in this base. The rest are primitive-free: `alert` is transcribed from upstream's Base UI source and verified byte-identical to it; `aspect-ratio`, `card`, `empty`, `native-select`, `skeleton` and `spinner` are DERIVED from the react-aria base and are NOT yet verified against upstream, which each file's header records. Nothing primitive-backed is ported: Base UI's primitive API is structurally different from React Aria's, so it cannot be derived and needs transcribed upstream sources. The foundations now run on real Base UI primitives: `button` (Button), `input` (Field.Control), `label` (Field.Label) and `separator` (Separator), alongside plain-host `textarea` and `kbd` — Base UI ships no textarea or Keyboard primitive, so those match the radix base's shape rather than the aria base's. `separator` deliberately takes the ARIA base's class string, not the radix one: Base UI emits `aria-orientation`, so radix's `data-horizontal:` utilities would never match and the separator would render with no thickness. `button` omits the aria base's data-variant/data-size, which upstream's Base UI base also omits, and ships no LinkButton because Base UI has no Link primitive — which is why `pagination` remains unported. `alert-dialog` is transcribed from upstream and is the first portalled family here (Overlay->Backdrop, Content->Popup, Cancel->Close); it drops upstream's `cn-font-heading` on the title, matching the react-aria base, because this package ships the utilities-inlined flavor and nothing defines that semantic hook. The form controls (`checkbox`, `switch`, `radio-group`) adapt their conditional utilities rather than copying them: Base UI publishes bare `data-checked`/`data-unchecked` where radix publishes `data-state=\"checked\"`, and every Root renders a `<span role=…>` that is never `:disabled`, so `disabled:` variants become `data-disabled:`. Both dialects are pinned by tests asserting the rendered DOM carries the attributes the class strings target. The overlays (`dialog`, `popover`, `tooltip`, plus `alert-dialog`) adapt positioning: Base UI inserts a Positioner layer (Portal > Positioner > Popup) and names its transform origin `--transform-origin`, not radix's `--radix-<part>-content-transform-origin`, so a copied radix class would reference a variable nothing sets and the popup would scale from the wrong corner. Tooltip also drops radix's `data-[state=delayed-open]` utilities, which have no Base UI counterpart. `PopoverAnchor` is absent because Base UI positions via the Positioner's `anchor` prop rather than an Anchor element — recorded in tests/cross-base.test.ts.",
|
|
394
412
|
"divergences": [
|
|
395
413
|
"No `\"use client\"` directives anywhere: octane has no Server Components, so the RSC axis does not exist here.",
|
|
396
414
|
"Refs are props (octane has no forwardRef) — upstream v4 already dropped forwardRef, so component shapes match.",
|
|
@@ -407,11 +425,11 @@
|
|
|
407
425
|
"The Base UI base's derived components carry unverified class strings. This is tracked rather than assumed safe: the bases genuinely diverge (the radix base's Skeleton is `bg-accent` where react-aria's is `bg-muted`), so identity is verified per component. `badge` was deliberately NOT derived — the react-aria version takes `render` as a function while Base UI's takes an element, so deriving it would ship the wrong API."
|
|
408
426
|
],
|
|
409
427
|
"ssr": "Tier 1 is fully server-rendered and tested (17 families through renderToString with no browser globals, including Slot-composed hosts), with hydration adoption pinned for representative shapes (plain host, Button-asChild anchor, nested Table) — zero mismatch, preserved node identity. Tier 2's portal-free components (Checkbox, RadioGroup, Switch, Slider, Tabs, Toggle, ToggleGroup, Accordion, Collapsible, AspectRatio, Progress) are server-rendered and tested. Field is SSR-safe (no portals/browser globals); Sidebar server-renders its static desktop branch (useIsMobile is false on the server; the mobile Sheet branch and tooltip portals are client-only). Portal-backed overlays/menus/Select are excluded until the radix binding supports overlay SSR; ScrollArea awaits verification of its viewport style injection on the server.",
|
|
410
|
-
"verified": "2026-08-
|
|
428
|
+
"verified": "2026-08-02"
|
|
411
429
|
},
|
|
412
430
|
{
|
|
413
431
|
"name": "@octanejs/sonner",
|
|
414
|
-
"version": "0.1.
|
|
432
|
+
"version": "0.1.20",
|
|
415
433
|
"category": "UI and interaction",
|
|
416
434
|
"upstream": { "package": "sonner", "version": "2.0.7" },
|
|
417
435
|
"surface": "Complete against the published `sonner@2.0.7` public surface: `Toaster`, the callable `toast` API and all methods, `useSonner`, promise lifecycle, multiple toaster targeting, stacked layout, themes, styling, focus management, timers, and swipe dismissal.",
|
|
@@ -425,7 +443,7 @@
|
|
|
425
443
|
},
|
|
426
444
|
{
|
|
427
445
|
"name": "@octanejs/streamdown",
|
|
428
|
-
"version": "0.1.
|
|
446
|
+
"version": "0.1.4",
|
|
429
447
|
"category": "Forms and content",
|
|
430
448
|
"upstream": { "package": "streamdown", "version": "2.5.0" },
|
|
431
449
|
"surface": "Complete Streamdown 2.5.0 root runtime and public type surface, plus the official code 1.1.1, math 1.0.2, Mermaid 1.0.2, and CJK 1.0.3 plugins exposed through ./code, ./math, ./mermaid, and ./cjk.",
|
|
@@ -438,7 +456,7 @@
|
|
|
438
456
|
},
|
|
439
457
|
{
|
|
440
458
|
"name": "@octanejs/styled-components",
|
|
441
|
-
"version": "0.1.
|
|
459
|
+
"version": "0.1.17",
|
|
442
460
|
"category": "Styling, tests, and devtools",
|
|
443
461
|
"upstream": { "package": "styled-components", "version": "6.4.3" },
|
|
444
462
|
"surface": "Full v6 web API, ported from the upstream 6.4.3 sources: `styled` with every HTML/SVG tag shortcut, `.attrs`/`.withConfig` chaining, `css`, `keyframes`, `createGlobalStyle`, `createTheme`, `ThemeProvider`/`ThemeContext`/`ThemeConsumer`/`useTheme`/`withTheme`, `StyleSheetManager`/`StyleSheetContext`/`StyleSheetConsumer` (targets, namespaces, vendor prefixing, stylis plugins, `shouldForwardProp`), `ServerStyleSheet`, `isStyledComponent`, `version`, and `__PRIVATE__`. Component selectors, folding (`styled(Styled)`), transient `$` props, `as`/`forwardedAs`, and the grouped CSSOM sheet engine (with upstream `data-styled` rehydration) all behave as upstream. The React Native surface and the RSC-only `stylisPluginRSC` are not ported.",
|
|
@@ -457,7 +475,7 @@
|
|
|
457
475
|
},
|
|
458
476
|
{
|
|
459
477
|
"name": "@octanejs/stylex",
|
|
460
|
-
"version": "0.1.
|
|
478
|
+
"version": "0.1.24",
|
|
461
479
|
"category": "Styling, tests, and devtools",
|
|
462
480
|
"upstream": { "package": "@stylexjs/stylex", "version": "0.19.0" },
|
|
463
481
|
"surface": "Full compile-time integration: re-exports the StyleX runtime API (`create`, `props`, `attrs`, `keyframes`, `defineVars`, `createTheme`) and registers as an import source; the `/vite` plugin runs the StyleX compiler over octane's compiled output and emits one static atomic stylesheet (`virtual:stylex.css`) with zero StyleX runtime in the bundle.",
|
|
@@ -470,7 +488,7 @@
|
|
|
470
488
|
},
|
|
471
489
|
{
|
|
472
490
|
"name": "@octanejs/tanstack-ai",
|
|
473
|
-
"version": "0.0.
|
|
491
|
+
"version": "0.0.19",
|
|
474
492
|
"category": "AI, data, and routing",
|
|
475
493
|
"upstream": { "package": "@tanstack/ai-react", "version": "0.17.0" },
|
|
476
494
|
"surface": "Ports the @tanstack/ai-react 0.17.0 hook surface (useChat, useRealtimeChat, useGeneration, useGenerateImage/Audio/Speech/Video, useTranscription, useSummarize, useAudioRecorder, useMcpAppBridge) while reusing @tanstack/ai 0.41.0 and @tanstack/ai-client 0.21.0 unchanged and mirroring all 30 @tanstack/ai-client convenience re-exports from the upstream index.",
|
|
@@ -489,7 +507,7 @@
|
|
|
489
507
|
},
|
|
490
508
|
{
|
|
491
509
|
"name": "@octanejs/tanstack-devtools",
|
|
492
|
-
"version": "0.0.
|
|
510
|
+
"version": "0.0.19",
|
|
493
511
|
"category": "Styling, tests, and devtools",
|
|
494
512
|
"upstream": { "package": "@tanstack/react-devtools", "version": "0.10.7" },
|
|
495
513
|
"surface": "Ports the @tanstack/react-devtools 0.10.7 public surface (the `TanStackDevtools` component plus its plugin/init types) onto Octane while reusing the framework-agnostic `@tanstack/devtools` 0.12.5 core (`TanStackDevtoolsCore`) unchanged. Plugin, title, and custom-trigger content authored as Octane elements is portaled into the containers the core creates.",
|
|
@@ -504,7 +522,7 @@
|
|
|
504
522
|
},
|
|
505
523
|
{
|
|
506
524
|
"name": "@octanejs/tanstack-form",
|
|
507
|
-
"version": "0.0.
|
|
525
|
+
"version": "0.0.19",
|
|
508
526
|
"category": "Forms and content",
|
|
509
527
|
"upstream": { "package": "@tanstack/react-form", "version": "1.33.2" },
|
|
510
528
|
"surface": "Ports the complete @tanstack/react-form 1.33.2 adapter surface (`useForm`, `useField`, form and field groups, hook contexts and component composition) while re-exporting @tanstack/form-core 1.33.2 unchanged and using @octanejs/tanstack-store for subscriptions.",
|
|
@@ -518,7 +536,7 @@
|
|
|
518
536
|
},
|
|
519
537
|
{
|
|
520
538
|
"name": "@octanejs/tanstack-hotkeys",
|
|
521
|
-
"version": "0.0.
|
|
539
|
+
"version": "0.0.14",
|
|
522
540
|
"category": "UI and interaction",
|
|
523
541
|
"upstream": { "package": "@tanstack/react-hotkeys", "version": "0.10.0" },
|
|
524
542
|
"surface": "Complete: the full upstream hook surface (`useHotkey`, `useHotkeys`, `useHeldKeys`, `useHeldKeyCodes`, `useKeyHold`, `useHotkeySequence`, `useHotkeySequences`, `useHotkeyRecorder`, `useHotkeySequenceRecorder`, `useHotkeyRegistrations`) plus `HotkeysProvider`/`useHotkeysContext`/`useDefaultHotkeysOptions`, re-exporting the framework-agnostic `@tanstack/hotkeys@0.8.0` core unchanged; store subscriptions go through `@octanejs/tanstack-store`.",
|
|
@@ -530,7 +548,7 @@
|
|
|
530
548
|
},
|
|
531
549
|
{
|
|
532
550
|
"name": "@octanejs/tanstack-pacer",
|
|
533
|
-
"version": "0.0.
|
|
551
|
+
"version": "0.0.14",
|
|
534
552
|
"category": "UI and interaction",
|
|
535
553
|
"upstream": { "package": "@tanstack/react-pacer", "version": "0.22.1" },
|
|
536
554
|
"surface": "Complete: every upstream hook family — debouncer (`useDebouncer`, `useDebouncedState`, `useDebouncedValue`, `useDebouncedCallback`), throttler, rate-limiter, queuer, batcher, and their async variants (async-debouncer, async-throttler, async-rate-limiter, async-queuer, async-batcher) — plus `PacerProvider`/`usePacerContext`/`useDefaultPacerOptions`, the per-instance `Subscribe` render-prop component, and the upstream subpath exports (`/debouncer`, `/async-retryer`, `/types`, `/utils`, ...), re-exporting the framework-agnostic `@tanstack/pacer@0.21.1` core unchanged.",
|
|
@@ -542,7 +560,7 @@
|
|
|
542
560
|
},
|
|
543
561
|
{
|
|
544
562
|
"name": "@octanejs/tanstack-query",
|
|
545
|
-
"version": "0.1.
|
|
563
|
+
"version": "0.1.24",
|
|
546
564
|
"category": "AI, data, and routing",
|
|
547
565
|
"upstream": { "package": "@tanstack/react-query", "version": "5.101.3" },
|
|
548
566
|
"surface": "Complete: 58/58 runtime exports plus the full TypeScript surface; the export surface is byte-identical to upstream in both directions (locked by test), and `@tanstack/query-core` is re-exported verbatim.",
|
|
@@ -554,7 +572,7 @@
|
|
|
554
572
|
},
|
|
555
573
|
{
|
|
556
574
|
"name": "@octanejs/tanstack-router",
|
|
557
|
-
"version": "0.1.
|
|
575
|
+
"version": "0.1.24",
|
|
558
576
|
"category": "AI, data, and routing",
|
|
559
577
|
"upstream": { "package": "@tanstack/react-router", "version": "1.170.18" },
|
|
560
578
|
"surface": "Octane's TanStack Router binding: typed route factories and hooks, the full Match pipeline and lifecycle, file routes with TSRX-aware generator integration, full Link navigation/preloading/masking behavior, blocking, Await/deferred hydration, scroll restoration, lazy routes, not-found handling, document/head assets, and client/server SSR entries.",
|
|
@@ -568,7 +586,7 @@
|
|
|
568
586
|
},
|
|
569
587
|
{
|
|
570
588
|
"name": "@octanejs/tanstack-router-ssr-query",
|
|
571
|
-
"version": "0.0.
|
|
589
|
+
"version": "0.0.14",
|
|
572
590
|
"category": "AI, data, and routing",
|
|
573
591
|
"upstream": { "package": "@tanstack/react-router-ssr-query", "version": "1.167.1" },
|
|
574
592
|
"surface": "Complete: `setupRouterSsrQueryIntegration` (the package's only export) delegating to `@tanstack/router-ssr-query-core@1.169.1`, with the QueryClientProvider Wrap composition on Octane.",
|
|
@@ -578,7 +596,7 @@
|
|
|
578
596
|
},
|
|
579
597
|
{
|
|
580
598
|
"name": "@octanejs/tanstack-store",
|
|
581
|
-
"version": "0.0.
|
|
599
|
+
"version": "0.0.19",
|
|
582
600
|
"category": "Shared state",
|
|
583
601
|
"upstream": { "package": "@tanstack/react-store", "version": "0.11.0" },
|
|
584
602
|
"surface": "Re-exports `@tanstack/store@0.11.0` unchanged and implements the stable React binding surface (`useSelector`, `useAtom`, `useCreateAtom`, `useCreateStore`, `createStoreContext`, and deprecated `useStore`) on Octane hooks.",
|
|
@@ -590,7 +608,7 @@
|
|
|
590
608
|
},
|
|
591
609
|
{
|
|
592
610
|
"name": "@octanejs/tanstack-table",
|
|
593
|
-
"version": "0.1.
|
|
611
|
+
"version": "0.1.22",
|
|
594
612
|
"category": "Data-heavy screens",
|
|
595
613
|
"upstream": { "package": "@tanstack/react-table", "version": "9.0.0-beta.58" },
|
|
596
614
|
"surface": "Complete port of the v9 adapter: the framework-agnostic `@tanstack/table-core` (constructTable + every tree-shakeable feature and row model) is reused verbatim, and the adapter — `useTable`, `Subscribe`, `flexRender`/`FlexRender`, `createTableHook`, `createTableHookContexts` — is transcribed onto octane hooks. Table state lives in TanStack Store atoms via the `coreReactivityFeature` bindings, and `useSelector` drives re-renders from the selected slice. Every store primitive (hooks, `createAtom`, `batch`, `shallow`, and the atom/store types) is imported from @octanejs/tanstack-store, which re-exports all of @tanstack/store — the binding takes no direct dependency on the store core, so there is only one path to it and atom identity cannot be split across duplicate copies.",
|
|
@@ -603,7 +621,7 @@
|
|
|
603
621
|
},
|
|
604
622
|
{
|
|
605
623
|
"name": "@octanejs/tanstack-virtual",
|
|
606
|
-
"version": "0.1.
|
|
624
|
+
"version": "0.1.22",
|
|
607
625
|
"category": "Data-heavy screens",
|
|
608
626
|
"upstream": { "package": "@tanstack/react-virtual", "version": "3.14.5" },
|
|
609
627
|
"surface": "Complete 1:1 port: the framework-agnostic `@tanstack/virtual-core` (Virtualizer + observers + windowing math) is reused verbatim; the React adapter (`useVirtualizer`, `useWindowVirtualizer`, incl. `useFlushSync` and the experimental `directDomUpdates` surface) is transcribed onto octane hooks, preserving upstream's force-update + flushSync-on-sync-scroll wiring and layout-effect lifecycle.",
|
|
@@ -615,7 +633,7 @@
|
|
|
615
633
|
},
|
|
616
634
|
{
|
|
617
635
|
"name": "@octanejs/tauri",
|
|
618
|
-
"version": "0.0.
|
|
636
|
+
"version": "0.0.8",
|
|
619
637
|
"category": "Desktop",
|
|
620
638
|
"upstream": { "package": "@tauri-apps/api", "version": "2.11.1" },
|
|
621
639
|
"surface": "Octane hooks over the framework-neutral Tauri IPC surface: useInvoke (suspending command), useInvokeState (pending/success/error with refetch), and useTauriEvent (event subscription with lifecycle-safe teardown). The rest of @tauri-apps/api — window, webview, menu, tray, path, dpi, image, and the plugin packages — is already framework-neutral and is imported directly rather than re-exported here.",
|
|
@@ -633,7 +651,7 @@
|
|
|
633
651
|
},
|
|
634
652
|
{
|
|
635
653
|
"name": "@octanejs/testing-library",
|
|
636
|
-
"version": "0.1.
|
|
654
|
+
"version": "0.1.22",
|
|
637
655
|
"category": "Styling, tests, and devtools",
|
|
638
656
|
"upstream": { "package": "@testing-library/react", "version": "unpinned" },
|
|
639
657
|
"surface": "`render`/`rerender`/`cleanup`/`renderHook` + `act` over the verbatim `@testing-library/dom` (every query, `screen`, `within`, `waitFor`, `fireEvent`, `prettyDOM`, `configure`), with commit timing wired to octane's scheduler via the dom-library's `eventWrapper`/`asyncWrapper` config.",
|
|
@@ -646,7 +664,7 @@
|
|
|
646
664
|
},
|
|
647
665
|
{
|
|
648
666
|
"name": "@octanejs/three",
|
|
649
|
-
"version": "0.1.
|
|
667
|
+
"version": "0.1.18",
|
|
650
668
|
"category": "Web 3D",
|
|
651
669
|
"upstream": { "package": "@react-three/fiber", "version": "9.6.1 (2a528745)" },
|
|
652
670
|
"surface": "Technical-preview Milestones 0–10 surface: renderer configuration and the DOM Canvas boundary, compiler ABI and renderer-local Three intrinsic types, catalogue and both extend forms, primitive/args construction, Three prop application, attachment, ordered placement/recreation, retained visibility, lifecycle/ref delivery, ownership-aware disposal, promise-returning HTMLCanvasElement and OffscreenCanvas roots, Octane act/flushSync scheduling, callback-aware unmountComponentAtNode, callable root state, scene/camera/raycaster and resize/DPR/viewport configuration, shadows/colors, one shared frame loop, controlled WebXR loop handoff, context-restore invalidation, compatible/reconstructing HMR, global effects, useStore/useThree/useFrame/useGraph and managed-instance helpers, the ray/pointer event system with DOM sources and custom managers, a keyed useLoader cache with preload/clear and GLTF graph augmentation, retained Suspense/Activity behavior, client Three-to-DOM pending/error projection, same-renderer createPortal targets with state/event enclaves and physical Three event bubbling, client-only Canvas shell streaming and production Vite/Rsbuild hydration adoption with the matching raw Rspack graph split, the explicit-target low-level DOMRegion boundary, a deterministic testing harness, an asynchronously acknowledged structured-clone transport proof, a checked public API/subpath matrix, Three r156/current compatibility lanes, a packed external consumer, real WebGL failure/recovery coverage, and semantic-checksummed renderer and shipped-size benchmarks.",
|
|
@@ -667,7 +685,7 @@
|
|
|
667
685
|
},
|
|
668
686
|
{
|
|
669
687
|
"name": "@octanejs/tiptap",
|
|
670
|
-
"version": "0.0.
|
|
688
|
+
"version": "0.0.19",
|
|
671
689
|
"category": "Forms and content",
|
|
672
690
|
"upstream": { "package": "@tiptap/react", "version": "3.28.0" },
|
|
673
691
|
"surface": "Complete @tiptap/react 3.28.0 adapter surface across the root and ./menus entries: @tiptap/core re-exports, editor hooks and contexts, the EditorContent portal bridge, compound Tiptap API, ReactRenderer, custom NodeView/MarkView renderers and helpers, BubbleMenu, and FloatingMenu.",
|
|
@@ -684,7 +702,7 @@
|
|
|
684
702
|
},
|
|
685
703
|
{
|
|
686
704
|
"name": "@octanejs/usehooks-ts",
|
|
687
|
-
"version": "0.0.
|
|
705
|
+
"version": "0.0.6",
|
|
688
706
|
"category": "UI and interaction",
|
|
689
707
|
"upstream": {
|
|
690
708
|
"package": "usehooks-ts",
|
|
@@ -702,7 +720,7 @@
|
|
|
702
720
|
},
|
|
703
721
|
{
|
|
704
722
|
"name": "@octanejs/valtio",
|
|
705
|
-
"version": "0.1.
|
|
723
|
+
"version": "0.1.8",
|
|
706
724
|
"category": "Shared state",
|
|
707
725
|
"upstream": { "package": "valtio", "version": "2.3.2" },
|
|
708
726
|
"surface": "The framework-agnostic `valtio/vanilla` core and `valtio/vanilla/utils` are re-exported verbatim; `useSnapshot` and the `useProxy` utility are ported to Octane.",
|
|
@@ -714,7 +732,7 @@
|
|
|
714
732
|
},
|
|
715
733
|
{
|
|
716
734
|
"name": "@octanejs/visx",
|
|
717
|
-
"version": "0.1.
|
|
735
|
+
"version": "0.1.19",
|
|
718
736
|
"category": "Data-heavy screens",
|
|
719
737
|
"upstream": { "package": "@visx/visx", "version": "4.0.0 + master@485c035" },
|
|
720
738
|
"surface": "Complete current Visx 4.x web runtime surface: the exact 35-namespace aggregate, all 40 feature entry points, and the eight public a11y/react, a11y/server, axis/react, scale/react, shape/react, theme/react, tooltip/floating, and voronoi/react subpaths. Released-only packages chord, delaunay, react-spring, sankey, and stats remain directly importable exactly as upstream specifies.",
|
|
@@ -730,7 +748,7 @@
|
|
|
730
748
|
},
|
|
731
749
|
{
|
|
732
750
|
"name": "@octanejs/wagmi",
|
|
733
|
-
"version": "0.0.
|
|
751
|
+
"version": "0.0.5",
|
|
734
752
|
"category": "Web3",
|
|
735
753
|
"upstream": { "package": "wagmi", "version": "3.7.4" },
|
|
736
754
|
"surface": "WagmiProvider and createConfig over @wagmi/core 3.6.4, with config, connection, connect, disconnect, switch-connection, switch-chain, connectors, connections, chains, balance, contract read/simulate/write, transaction send/wait, and message-signing hooks.",
|
|
@@ -746,7 +764,7 @@
|
|
|
746
764
|
},
|
|
747
765
|
{
|
|
748
766
|
"name": "@octanejs/zustand",
|
|
749
|
-
"version": "0.1.
|
|
767
|
+
"version": "0.1.24",
|
|
750
768
|
"category": "Shared state",
|
|
751
769
|
"upstream": { "package": "zustand", "version": "5.0.14" },
|
|
752
770
|
"surface": "Complete 1:1 port: the framework-agnostic vanilla store is reused verbatim; `create`/`useStore`, `shallow`/`useShallow`, the traditional equality-fn variants, and all middleware (persist, devtools, subscribeWithSelector, combine, redux).",
|