@arcanejs/react-toolkit 0.12.5 → 0.14.0
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 +273 -193
- package/dist/{chunk-Q54JXYCV.js → chunk-EJVUDIQP.js} +94 -38
- package/dist/{chunk-CAHNLXTY.mjs → chunk-TP2BI3OM.mjs} +93 -37
- package/dist/colors.js +7 -7
- package/dist/colors.mjs +1 -1
- package/dist/data.d.mts +12 -2
- package/dist/data.d.ts +12 -2
- package/dist/data.js +49 -12
- package/dist/data.mjs +47 -10
- package/dist/index.d.mts +2 -2
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/index.mjs +1 -1
- package/package.json +8 -8
package/README.md
CHANGED
|
@@ -2,309 +2,389 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/@arcanejs/react-toolkit)
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
for your single-process Node.js apps,
|
|
7
|
-
using a custom react renderer, and WebSockets.
|
|
5
|
+
React renderer for ArcaneJS server-side control panels.
|
|
8
6
|
|
|
9
|
-
|
|
10
|
-
browsers / devices / clients simultaneously,
|
|
11
|
-
and changes caused by any client will be immediately propagated to all other
|
|
12
|
-
clients.
|
|
13
|
-
|
|
14
|
-
The UI has also been designed primarily with touch devices in mind,
|
|
15
|
-
but also works well with a cursor and keyboard.
|
|
7
|
+
This package lets you build toolkit component trees using React state/hooks on the server, then synchronize them to connected browser clients in real time.
|
|
16
8
|
|
|
17
9
|
<p align="center">
|
|
18
10
|
<img src="https://raw.githubusercontent.com/ArcaneWizards/arcanejs/main/packages/react-toolkit/docs/architecture.svg" alt="Architecture Diagram">
|
|
19
11
|
</p>
|
|
20
12
|
|
|
21
|
-
##
|
|
13
|
+
## Why Use `@arcanejs/react-toolkit`
|
|
22
14
|
|
|
23
|
-
|
|
15
|
+
This package is useful when you want a control panel for a long-running Node.js process without building a full web app stack.
|
|
24
16
|
|
|
25
|
-
|
|
17
|
+
It gives you:
|
|
26
18
|
|
|
27
|
-
|
|
19
|
+
- server-side React state/hooks for control logic
|
|
20
|
+
- realtime multi-client sync over WebSockets
|
|
21
|
+
- a ready set of control-oriented UI components (switches, sliders, groups, tabs, timelines)
|
|
22
|
+
- an extension path for custom components when core components are not enough
|
|
28
23
|
|
|
29
|
-
|
|
24
|
+
Typical use cases:
|
|
30
25
|
|
|
31
|
-
-
|
|
26
|
+
- home/lab automation control surfaces
|
|
27
|
+
- AV/lighting/operations dashboards on a local network
|
|
28
|
+
- internal tooling for stateful services/scripts that need live operator controls
|
|
32
29
|
|
|
33
|
-
##
|
|
30
|
+
## When It Fits (And When It Doesn't)
|
|
34
31
|
|
|
35
|
-
|
|
36
|
-
and would like to have a way to interact with the state or configuration
|
|
37
|
-
of these applications in real-time,
|
|
38
|
-
for example:
|
|
32
|
+
Good fit:
|
|
39
33
|
|
|
40
|
-
-
|
|
41
|
-
-
|
|
34
|
+
- single-process Node.js apps with in-memory state
|
|
35
|
+
- trusted/internal networks where operators need live controls
|
|
36
|
+
- projects where React composition is preferred over manual tree mutation
|
|
42
37
|
|
|
43
|
-
|
|
38
|
+
Not a fit:
|
|
44
39
|
|
|
45
|
-
|
|
46
|
-
|
|
40
|
+
- internet-exposed apps requiring built-in auth/authz
|
|
41
|
+
- horizontally scaled/multi-process architectures needing shared state coordination
|
|
42
|
+
- general-purpose public web apps that need standard React DOM/SSR patterns
|
|
47
43
|
|
|
48
|
-
|
|
49
|
-
- Is stateless _(It's explicitly designed to manage in-memory state)_
|
|
50
|
-
- Will be exposed over the internet _(no authentication has been implemented)_
|
|
44
|
+
## Install
|
|
51
45
|
|
|
52
|
-
|
|
46
|
+
```bash
|
|
47
|
+
npm install react@^19.2.0 @arcanejs/toolkit @arcanejs/react-toolkit
|
|
48
|
+
```
|
|
53
49
|
|
|
54
|
-
|
|
50
|
+
Version compatibility:
|
|
55
51
|
|
|
56
|
-
|
|
57
|
-
npm install --save react@^18 @arcanejs/toolkit @arcanejs/react-toolkit
|
|
58
|
-
```
|
|
52
|
+
- `@arcanejs/react-toolkit` is tested against React 19 and `react-reconciler` 0.33.x.
|
|
59
53
|
|
|
60
|
-
|
|
54
|
+
Optional helper dependencies:
|
|
61
55
|
|
|
62
|
-
-
|
|
63
|
-
- We don't need `react-dom` or any react native libraries,
|
|
64
|
-
`@arcanejs/react-toolkit` is the react renderer.
|
|
56
|
+
- `zod` is required if you use `@arcanejs/react-toolkit/data`
|
|
65
57
|
|
|
66
|
-
|
|
67
|
-
server-side state like this:
|
|
58
|
+
## Quick Start
|
|
68
59
|
|
|
69
|
-
```
|
|
60
|
+
```tsx
|
|
70
61
|
import { useState } from 'react';
|
|
71
62
|
import { Toolkit } from '@arcanejs/toolkit';
|
|
72
|
-
import {
|
|
63
|
+
import {
|
|
64
|
+
ToolkitRenderer,
|
|
65
|
+
Group,
|
|
66
|
+
Switch,
|
|
67
|
+
SliderButton,
|
|
68
|
+
} from '@arcanejs/react-toolkit';
|
|
73
69
|
|
|
74
70
|
const toolkit = new Toolkit();
|
|
71
|
+
toolkit.start({ mode: 'automatic', port: 3000 });
|
|
75
72
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
toolkit.start({
|
|
80
|
-
mode: 'automatic',
|
|
81
|
-
port: 3000,
|
|
82
|
-
});
|
|
83
|
-
|
|
84
|
-
const ControlPanel = () => {
|
|
85
|
-
const [switchState, setSwitchState] = useState<'off' | 'on'>('off');
|
|
86
|
-
const [sliderValue, setSliderValue] = useState(50);
|
|
73
|
+
function App() {
|
|
74
|
+
const [enabled, setEnabled] = useState<'on' | 'off'>('off');
|
|
75
|
+
const [level, setLevel] = useState(50);
|
|
87
76
|
|
|
88
77
|
return (
|
|
89
|
-
<Group direction=
|
|
78
|
+
<Group direction="vertical" title="Controller">
|
|
90
79
|
<Group>
|
|
91
|
-
{`
|
|
92
|
-
<Switch
|
|
93
|
-
value={switchState}
|
|
94
|
-
onChange={setSwitchState}
|
|
95
|
-
/>
|
|
80
|
+
{`Enabled: ${enabled}`}
|
|
81
|
+
<Switch value={enabled} onChange={setEnabled} />
|
|
96
82
|
</Group>
|
|
97
83
|
<Group>
|
|
98
|
-
{`
|
|
99
|
-
<SliderButton
|
|
100
|
-
value={sliderValue}
|
|
101
|
-
onChange={setSliderValue}
|
|
102
|
-
min={0}
|
|
103
|
-
max={100}
|
|
104
|
-
/>
|
|
84
|
+
{`Level: ${level}`}
|
|
85
|
+
<SliderButton value={level} onChange={setLevel} min={0} max={100} />
|
|
105
86
|
</Group>
|
|
106
87
|
</Group>
|
|
107
88
|
);
|
|
108
|
-
}
|
|
89
|
+
}
|
|
109
90
|
|
|
110
|
-
|
|
111
|
-
ToolkitRenderer.render(<ControlPanel />, toolkit);
|
|
91
|
+
ToolkitRenderer.render(<App />, toolkit);
|
|
112
92
|
```
|
|
113
93
|
|
|
114
|
-
|
|
115
|
-
from [localhost:3000](http://localhost:3000):
|
|
94
|
+
## Core Exports
|
|
116
95
|
|
|
117
|
-
|
|
96
|
+
### From `@arcanejs/react-toolkit`
|
|
118
97
|
|
|
119
|
-
|
|
98
|
+
- `ToolkitRenderer`
|
|
99
|
+
- `render(element, toolkit, rootGroupProps?, config?)`
|
|
100
|
+
- `renderGroup(element, group, config?)`
|
|
101
|
+
- Core React components:
|
|
102
|
+
- `Button`, `Group`, `GroupHeader`, `Label`, `Rect`, `SliderButton`, `Switch`, `Tab`, `Tabs`, `TextInput`, `Timeline`
|
|
103
|
+
- Extension helpers:
|
|
104
|
+
- `prepareComponents(namespace, components)`
|
|
105
|
+
- `CoreComponents`
|
|
120
106
|
|
|
121
|
-
|
|
122
|
-
or components, only `@arcanejs` components are supported.
|
|
107
|
+
Core component props/events map directly to the corresponding classes in `@arcanejs/toolkit`.
|
|
123
108
|
|
|
124
|
-
|
|
125
|
-
in the same manner that you would any `react-dom` or `react-native` project.
|
|
109
|
+
## Core Component API
|
|
126
110
|
|
|
127
|
-
|
|
111
|
+
These are the React components most users consume directly from `@arcanejs/react-toolkit`.
|
|
128
112
|
|
|
129
|
-
|
|
130
|
-
and does not accurately represent the HTML used on the frontend.
|
|
131
|
-
Your `@arcanejs` tree is converted to a JSON representation,
|
|
132
|
-
and then sent to clients / browsers over a WebSocket.
|
|
133
|
-
There is then a separate `react-dom` application
|
|
134
|
-
that is loaded in the browser,
|
|
135
|
-
and then used to render the JSON representation of the `@arcanejs` tree.
|
|
136
|
-
|
|
137
|
-
- It's possible to define your own custom components,
|
|
138
|
-
however this functionality is not documented.
|
|
139
|
-
|
|
140
|
-
You can see code examples of this functionality here in the
|
|
141
|
-
[custom components example app](https://github.com/ArcaneWizards/arcanejs/tree/main/examples/custom-components).
|
|
113
|
+
### `Button`
|
|
142
114
|
|
|
143
|
-
|
|
115
|
+
Props:
|
|
144
116
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
117
|
+
- `text?: string | null`
|
|
118
|
+
- `icon?: string | null` (Material Symbols Outlined icon name)
|
|
119
|
+
- `mode?: 'normal' | 'pressed'`
|
|
120
|
+
- `error?: string | null`
|
|
121
|
+
- `onClick?: (connection) => void | Promise<void>`
|
|
148
122
|
|
|
149
|
-
|
|
123
|
+
Notes:
|
|
150
124
|
|
|
151
|
-
|
|
125
|
+
- `onClick` uses request/response semantics and can be async.
|
|
126
|
+
- Throwing (or rejecting) in `onClick` surfaces an error state in the UI.
|
|
152
127
|
|
|
153
|
-
|
|
128
|
+
```tsx
|
|
129
|
+
<Button
|
|
130
|
+
text="Run"
|
|
131
|
+
icon="play_arrow"
|
|
132
|
+
onClick={async () => {
|
|
133
|
+
await runTask();
|
|
134
|
+
}}
|
|
135
|
+
/>
|
|
136
|
+
```
|
|
154
137
|
|
|
155
|
-
|
|
138
|
+
### `Group`
|
|
156
139
|
|
|
157
|
-
|
|
140
|
+
Props:
|
|
158
141
|
|
|
159
|
-
|
|
160
|
-
|
|
142
|
+
- `direction?: 'horizontal' | 'vertical'`
|
|
143
|
+
- `wrap?: boolean`
|
|
144
|
+
- `border?: true`
|
|
145
|
+
- `title?: string | null`
|
|
146
|
+
- `labels?: Array<{ text: string }> | null`
|
|
147
|
+
- `editableTitle?: boolean`
|
|
148
|
+
- `defaultCollapsibleState?: 'open' | 'closed' | 'auto'`
|
|
149
|
+
- `onTitleChanged?: (title, connection) => void`
|
|
161
150
|
|
|
162
|
-
|
|
151
|
+
Use `Group` as the primary layout primitive and nest groups freely.
|
|
163
152
|
|
|
164
|
-
|
|
165
|
-
|
|
153
|
+
```tsx
|
|
154
|
+
<Group
|
|
155
|
+
direction="vertical"
|
|
156
|
+
title="Fixture Settings"
|
|
157
|
+
border
|
|
158
|
+
editableTitle
|
|
159
|
+
onTitleChanged={(title) => setPanelTitle(title)}
|
|
160
|
+
>
|
|
161
|
+
<Label text="Master Intensity" />
|
|
162
|
+
<SliderButton value={master} min={0} max={100} onChange={setMaster} />
|
|
163
|
+
</Group>
|
|
164
|
+
```
|
|
166
165
|
|
|
167
|
-
|
|
166
|
+
### `GroupHeader`
|
|
168
167
|
|
|
169
|
-
|
|
168
|
+
`GroupHeader` lets you place controls in a group's header area.
|
|
170
169
|
|
|
171
|
-
|
|
170
|
+
```tsx
|
|
171
|
+
<Group title="Playlist">
|
|
172
|
+
<GroupHeader>
|
|
173
|
+
<Button text="Add" onClick={addItem} />
|
|
174
|
+
<Button text="Shuffle" onClick={shuffle} />
|
|
175
|
+
</GroupHeader>
|
|
176
|
+
<Label text="Current Queue" />
|
|
177
|
+
</Group>
|
|
178
|
+
```
|
|
172
179
|
|
|
173
|
-
|
|
180
|
+
### `Label`
|
|
174
181
|
|
|
175
|
-
|
|
176
|
-
to indicate an error and propagate an error message to the user,
|
|
177
|
-
similar to setting the `error` property.
|
|
182
|
+
Props:
|
|
178
183
|
|
|
179
|
-
|
|
184
|
+
- `text: string | null`
|
|
185
|
+
- `bold?: boolean`
|
|
180
186
|
|
|
181
187
|
```tsx
|
|
182
|
-
|
|
183
|
-
<Button text="Stop" onClick={() => doAThing()} icon="close" />
|
|
184
|
-
);
|
|
188
|
+
<Label text={`Connected: ${isConnected ? 'yes' : 'no'}`} bold />
|
|
185
189
|
```
|
|
186
190
|
|
|
187
|
-
### `
|
|
191
|
+
### `Rect`
|
|
192
|
+
|
|
193
|
+
Props:
|
|
188
194
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
and nest them to achieve the desired layout.
|
|
195
|
+
- `color?: string`
|
|
196
|
+
- `grow?: boolean`
|
|
192
197
|
|
|
193
|
-
|
|
198
|
+
Useful for color/state swatches.
|
|
199
|
+
|
|
200
|
+
```tsx
|
|
201
|
+
<Group>
|
|
202
|
+
<Label text="Current Color" />
|
|
203
|
+
<Rect color={`hsl(${hue}deg 100% 50%)`} grow />
|
|
204
|
+
</Group>
|
|
205
|
+
```
|
|
194
206
|
|
|
195
|
-
|
|
207
|
+
### `SliderButton`
|
|
196
208
|
|
|
197
|
-
|
|
209
|
+
Props:
|
|
198
210
|
|
|
199
|
-
|
|
211
|
+
- `value: number` (required)
|
|
212
|
+
- `min?: number` (default `0`)
|
|
213
|
+
- `max?: number` (default `255`)
|
|
214
|
+
- `step?: number` (default `5`)
|
|
215
|
+
- `defaultValue?: number` (for uncontrolled style workflows)
|
|
216
|
+
- `gradient?: Array<{ color: string; position: number }>`
|
|
217
|
+
- `grow?: boolean`
|
|
218
|
+
- `onChange?: (value, connection) => void | Promise<void>`
|
|
200
219
|
|
|
201
|
-
|
|
220
|
+
```tsx
|
|
221
|
+
<SliderButton
|
|
222
|
+
value={temperature}
|
|
223
|
+
min={2700}
|
|
224
|
+
max={6500}
|
|
225
|
+
step={100}
|
|
226
|
+
onChange={setTemperature}
|
|
227
|
+
/>
|
|
228
|
+
```
|
|
202
229
|
|
|
203
|
-
|
|
204
|
-
components will be wrapped, and start to be arranged on additional columns
|
|
205
|
-
or rows.
|
|
230
|
+
### `Switch`
|
|
206
231
|
|
|
207
|
-
|
|
232
|
+
Props:
|
|
208
233
|
|
|
209
|
-
|
|
210
|
-
|
|
234
|
+
- `value?: 'on' | 'off'`
|
|
235
|
+
- `defaultValue?: 'on' | 'off'`
|
|
236
|
+
- `onChange?: (state, connection) => void | Promise<void>`
|
|
211
237
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
238
|
+
```tsx
|
|
239
|
+
<Switch value={enabled} onChange={setEnabled} />
|
|
240
|
+
```
|
|
215
241
|
|
|
216
|
-
|
|
242
|
+
### `Tabs` and `Tab`
|
|
217
243
|
|
|
218
|
-
|
|
244
|
+
`Tabs` only accepts `Tab` children. Each `Tab` should contain one child component subtree.
|
|
219
245
|
|
|
220
|
-
|
|
246
|
+
```tsx
|
|
247
|
+
<Tabs>
|
|
248
|
+
<Tab name="Input">
|
|
249
|
+
<Group>
|
|
250
|
+
<TextInput value={input} onChange={setInput} />
|
|
251
|
+
</Group>
|
|
252
|
+
</Tab>
|
|
253
|
+
<Tab name="Output">
|
|
254
|
+
<Group>
|
|
255
|
+
<Label text={output} />
|
|
256
|
+
</Group>
|
|
257
|
+
</Tab>
|
|
258
|
+
</Tabs>
|
|
259
|
+
```
|
|
221
260
|
|
|
222
|
-
|
|
223
|
-
will allow the user to click on the title to change it,
|
|
224
|
-
which will trigger a callback to the listener `onTitleChanged`.
|
|
261
|
+
### `TextInput`
|
|
225
262
|
|
|
226
|
-
|
|
227
|
-
(optional, default: `undefined`)
|
|
263
|
+
Props:
|
|
228
264
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
by default set to the given state.
|
|
265
|
+
- `value?: string | null`
|
|
266
|
+
- `onChange?: (value, connection) => void | Promise<void>`
|
|
232
267
|
|
|
233
|
-
|
|
234
|
-
|
|
268
|
+
```tsx
|
|
269
|
+
<TextInput value={name} onChange={setName} />
|
|
270
|
+
```
|
|
235
271
|
|
|
236
|
-
|
|
272
|
+
### `Timeline`
|
|
237
273
|
|
|
238
|
-
|
|
274
|
+
Props:
|
|
239
275
|
|
|
240
|
-
|
|
276
|
+
- `state?`:
|
|
277
|
+
- `{ state: 'playing'; totalTimeMillis; effectiveStartTime; speed }`
|
|
278
|
+
- `{ state: 'stopped'; totalTimeMillis; currentTimeMillis }`
|
|
279
|
+
- `title?: string | null`
|
|
280
|
+
- `subtitles?: string[] | null`
|
|
281
|
+
- `source?: { name: string } | null`
|
|
241
282
|
|
|
242
|
-
|
|
283
|
+
```tsx
|
|
284
|
+
<Timeline
|
|
285
|
+
title="Show Timeline"
|
|
286
|
+
subtitles={[`Cue ${cue}`, `BPM ${bpm}`]}
|
|
287
|
+
source={{ name: activeTrack }}
|
|
288
|
+
state={
|
|
289
|
+
playing
|
|
290
|
+
? {
|
|
291
|
+
state: 'playing',
|
|
292
|
+
totalTimeMillis: duration,
|
|
293
|
+
effectiveStartTime: startedAt,
|
|
294
|
+
speed: 1,
|
|
295
|
+
}
|
|
296
|
+
: {
|
|
297
|
+
state: 'stopped',
|
|
298
|
+
totalTimeMillis: duration,
|
|
299
|
+
currentTimeMillis: pausedAt,
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
/>
|
|
303
|
+
```
|
|
243
304
|
|
|
244
|
-
|
|
245
|
-
`<GroupHeader/>` component directly under the `<Group/>`.
|
|
305
|
+
## Helper Modules
|
|
246
306
|
|
|
247
|
-
|
|
248
|
-
a `<Group>` component's direct children,
|
|
249
|
-
and all nested components will be placed in the header.
|
|
307
|
+
### `@arcanejs/react-toolkit/connections`
|
|
250
308
|
|
|
251
|
-
|
|
309
|
+
Connection-awareness for server-side React trees:
|
|
252
310
|
|
|
253
|
-
|
|
311
|
+
- `ConnectionsContext`
|
|
312
|
+
- `ConnectionsContextProvider`
|
|
254
313
|
|
|
255
|
-
|
|
314
|
+
Use this to render connection-specific UI or track per-connection state.
|
|
256
315
|
|
|
257
|
-
### `
|
|
316
|
+
### `@arcanejs/react-toolkit/data`
|
|
258
317
|
|
|
259
|
-
|
|
318
|
+
JSON file persistence helpers backed by Zod validation:
|
|
260
319
|
|
|
261
|
-
|
|
320
|
+
- `createDataFileDefinition`
|
|
321
|
+
- `useDataFileContext`
|
|
322
|
+
- `useDataFileData`
|
|
323
|
+
- `useDataFileUpdater`
|
|
324
|
+
- `useDataFile`
|
|
325
|
+
- `useDataFileCore`
|
|
262
326
|
|
|
263
|
-
|
|
327
|
+
Supports:
|
|
264
328
|
|
|
265
|
-
|
|
329
|
+
- lazy file creation with defaults
|
|
330
|
+
- schema validation on load
|
|
331
|
+
- throttled disk writes
|
|
332
|
+
- path switching behavior (`onPathChange: 'transfer' | 'defaultValue'`)
|
|
266
333
|
|
|
267
|
-
|
|
334
|
+
### `@arcanejs/react-toolkit/colors`
|
|
268
335
|
|
|
269
|
-
|
|
336
|
+
- `HslColor` type
|
|
337
|
+
- `HslColorPicker` component
|
|
270
338
|
|
|
271
|
-
|
|
339
|
+
### `@arcanejs/react-toolkit/logging`
|
|
272
340
|
|
|
273
|
-
|
|
341
|
+
- `LoggerContext`
|
|
342
|
+
- `useLogger()`
|
|
274
343
|
|
|
275
|
-
|
|
344
|
+
## Custom Component Namespaces
|
|
276
345
|
|
|
277
|
-
|
|
346
|
+
Use `prepareComponents(...)` + `ToolkitRenderer` config to add custom namespaces:
|
|
278
347
|
|
|
279
|
-
|
|
348
|
+
```tsx
|
|
349
|
+
import {
|
|
350
|
+
prepareComponents,
|
|
351
|
+
CoreComponents,
|
|
352
|
+
ToolkitRenderer,
|
|
353
|
+
} from '@arcanejs/react-toolkit';
|
|
354
|
+
|
|
355
|
+
const Custom = prepareComponents('custom', {
|
|
356
|
+
MyComponent,
|
|
357
|
+
});
|
|
280
358
|
|
|
281
|
-
|
|
359
|
+
ToolkitRenderer.render(
|
|
360
|
+
<App />,
|
|
361
|
+
toolkit,
|
|
362
|
+
{},
|
|
363
|
+
{
|
|
364
|
+
componentNamespaces: [CoreComponents, Custom],
|
|
365
|
+
},
|
|
366
|
+
);
|
|
367
|
+
```
|
|
282
368
|
|
|
283
|
-
|
|
369
|
+
You must also implement matching protocol/backend/frontend layers. See:
|
|
284
370
|
|
|
285
|
-
|
|
371
|
+
- <https://github.com/ArcaneWizards/arcanejs/tree/main/examples/custom-components>
|
|
286
372
|
|
|
287
|
-
|
|
288
|
-
please see the example directory in the arcane monorepo:
|
|
289
|
-
<https://github.com/ArcaneWizards/arcanejs/tree/main/examples/react>
|
|
373
|
+
## Important Constraints
|
|
290
374
|
|
|
291
|
-
|
|
375
|
+
- This is a custom renderer, not React DOM. Standard HTML elements are not supported.
|
|
376
|
+
- React state/hooks run on the server process.
|
|
377
|
+
- Toolkit root can only be set once.
|
|
378
|
+
- Architecture is single-process and intentionally stateful.
|
|
379
|
+
- No built-in authentication/authorization.
|
|
292
380
|
|
|
293
|
-
|
|
294
|
-
to create an app that can be used to control a Philips Hue Bridge
|
|
295
|
-
on your local network.
|
|
381
|
+
## Stability / Suitability
|
|
296
382
|
|
|
297
|
-
|
|
383
|
+
ArcaneJS uses `react-reconciler` APIs and is considered experimental. It is best suited for trusted-network/internal control applications.
|
|
298
384
|
|
|
299
|
-
|
|
300
|
-
and takes advantage of unstable `react` APIs exposed via `react-render`.
|
|
301
|
-
It's not suitable for production or commercial projects yet,
|
|
302
|
-
especially those that rely on regular updates of dependencies
|
|
303
|
-
for security reasons,
|
|
304
|
-
as usage of this project may make it difficult to keep `react` up-to-date
|
|
305
|
-
(that being said, the license does not prohibit this,
|
|
306
|
-
so feel free to at-your-own-risk).
|
|
385
|
+
## Examples
|
|
307
386
|
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
387
|
+
- React examples: <https://github.com/ArcaneWizards/arcanejs/tree/main/examples/react>
|
|
388
|
+
- Main readme example: <https://github.com/ArcaneWizards/arcanejs/blob/main/examples/react/src/readme.tsx>
|
|
389
|
+
- Connections example: <https://github.com/ArcaneWizards/arcanejs/blob/main/examples/react/src/connections.tsx>
|
|
390
|
+
- Data file example: <https://github.com/ArcaneWizards/arcanejs/blob/main/examples/react/src/data-file.tsx>
|