@ultrst-armada/floks 1.2.4 → 1.2.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 CHANGED
@@ -1,2 +1,12 @@
1
1
  # Floks
2
2
  This will be the counterpart Kloks but for the frontend components. This will store the canonical frontend elements that we will use in our Skipo projects.
3
+
4
+ ## FrnlTS architecture
5
+
6
+ Floks is the canonical concrete frontend element library built on FrnlTS. FrnlTS provides the semantic UI runtime; Floks provides concrete reusable elements and their visual behavior.
7
+
8
+ - UI Primitive: use for atomic elements such as a simple Button.
9
+ - UI Composite: use when an element requires multiple semantic children, Slots, coordinated state, or Flows. Complex Button variants may therefore be composites.
10
+ - Shared runtime: Floks UI assets use the canonical FrnlTS Zustand store and Frnl semantic boundaries.
11
+
12
+ FrnlTS is consumed as the published @dinilmusick/frnlts package. Floks owns the concrete UI vocabulary; FrnlTS owns the semantic model.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ultrst-armada/floks",
3
- "version": "1.2.4",
3
+ "version": "1.2.5",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public",
@@ -61,20 +61,26 @@
61
61
  "react-dom": "^18.0.0 || ^19.0.0"
62
62
  },
63
63
  "devDependencies": {
64
+ "@testing-library/jest-dom": "^7.0.1",
65
+ "@testing-library/react": "^16.3.3",
64
66
  "@types/node": "^22.0.0",
65
67
  "@types/react": "^19.0.0",
66
68
  "@types/react-dom": "^19.0.0",
67
69
  "@ultrst-armada/kloks": "^1.4.0",
70
+ "@ultrst-armada/frnlts": "^0.2.2",
71
+ "happy-dom": "^21.0.3",
68
72
  "lucide-react": "^1.55.0",
69
73
  "react": "^19.0.1",
70
74
  "react-dom": "^19.0.1",
71
75
  "tsup": "^8.4.0",
72
76
  "typescript": "^5.7.3",
73
- "vitest": "^3.1.0"
77
+ "vitest": "^3.1.0",
78
+ "zustand": "^5.0.3"
74
79
  },
75
80
  "pnpm": {
76
81
  "overrides": {
77
- "@ultrst-armada/kloks": "file:../Kloks"
82
+ "@ultrst-armada/kloks": "file:../Kloks",
83
+ "@ultrst-armada/frnlts": "file:../FrnlTS"
78
84
  }
79
85
  },
80
86
  "packageManager": "pnpm@9.15.0"
@@ -4,11 +4,6 @@ import {
4
4
  MiniMap,
5
5
  useCanvas
6
6
  } from "./chunk-34YRDZPJ.js";
7
- import {
8
- DockBar,
9
- DockItem,
10
- useDock
11
- } from "./chunk-6VB74RCH.js";
12
7
  import {
13
8
  BIOME_CONFIGS,
14
9
  COORD_SCALE,
@@ -25,17 +20,6 @@ import {
25
20
  useCanvas2DCoordinates,
26
21
  useCanvas2DMagnetic
27
22
  } from "./chunk-XVAHT5WE.js";
28
- import {
29
- MonacoEditor,
30
- skipoDarkTheme,
31
- useMonaco
32
- } from "./chunk-QTTWWBNN.js";
33
- import {
34
- Tile,
35
- TileCreateButton,
36
- TileGrid,
37
- useTileMenu
38
- } from "./chunk-SA3JADUG.js";
39
23
  import {
40
24
  PredictiveGhostNodeCard,
41
25
  PredictiveHudToolbar,
@@ -52,6 +36,22 @@ import {
52
36
  getSocketScreenPosition,
53
37
  renderSpriteIcon
54
38
  } from "./chunk-U2SC3RMJ.js";
39
+ import {
40
+ DockBar,
41
+ DockItem,
42
+ useDock
43
+ } from "./chunk-6VB74RCH.js";
44
+ import {
45
+ MonacoEditor,
46
+ skipoDarkTheme,
47
+ useMonaco
48
+ } from "./chunk-QTTWWBNN.js";
49
+ import {
50
+ Tile,
51
+ TileCreateButton,
52
+ TileGrid,
53
+ useTileMenu
54
+ } from "./chunk-SA3JADUG.js";
55
55
 
56
56
  // src/node-graph-ui/widgets/MidpointWidgets.tsx
57
57
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -319,7 +319,7 @@ var FLOKS_PUBLIC_SYMBOLS = [
319
319
  "SettingsTab",
320
320
  "WorkspaceTilesMenu",
321
321
  "UIParent",
322
- "PanelModel",
322
+ "floksFrnlStore",
323
323
  "PanelProps",
324
324
  // UI Base Primitives & Forms
325
325
  "BaseButton",
@@ -328,7 +328,7 @@ var FLOKS_PUBLIC_SYMBOLS = [
328
328
  "BaseSelect",
329
329
  "BaseToggle",
330
330
  "BaseBadge",
331
- "ButtonModel",
331
+ "FrnlStoreProvider",
332
332
  "ButtonProps",
333
333
  "CredentialField",
334
334
  "LoginModal",
@@ -1,3 +1,6 @@
1
+ import {
2
+ useFloksFrnlStore
3
+ } from "./chunk-X4B3EBMY.js";
1
4
  import {
2
5
  usePalette
3
6
  } from "./chunk-ZQK5I5ZL.js";
@@ -5,77 +8,8 @@ import {
5
8
  useBottomStrip
6
9
  } from "./chunk-62ITRDJX.js";
7
10
 
8
- // src/hierarchy/UIElementBase.ts
9
- import { GraphEntityParent } from "@ultrst-armada/kloks/state-graph";
10
- var activePaletteResolver = () => ({});
11
- function registerPaletteResolver(fn) {
12
- activePaletteResolver = fn;
13
- }
14
- function getActivePalette() {
15
- return activePaletteResolver();
16
- }
17
- var UIElementBase = class extends GraphEntityParent {
18
- themeRole;
19
- variant;
20
- size;
21
- isDisabled;
22
- isHighlighted;
23
- tooltipText;
24
- tooltipDir;
25
- constructor(id, name, themeRole, variant = "primary", size = "md", metadata = {}) {
26
- super(id, name, "ui", metadata);
27
- this.themeRole = themeRole;
28
- this.variant = variant;
29
- this.size = size;
30
- this.isDisabled = false;
31
- this.isHighlighted = false;
32
- this.tooltipDir = "bottom";
33
- }
34
- getPalette() {
35
- return getActivePalette();
36
- }
37
- setDisabled(disabled) {
38
- if (this.isDisabled !== disabled) {
39
- this.isDisabled = disabled;
40
- this.emit("disabled_changed", disabled);
41
- }
42
- }
43
- setHighlighted(highlighted) {
44
- if (this.isHighlighted !== highlighted) {
45
- this.isHighlighted = highlighted;
46
- this.emit("highlighted_changed", highlighted);
47
- }
48
- }
49
- setVariant(variant) {
50
- this.variant = variant;
51
- this.emit("variant_changed", variant);
52
- }
53
- initialize() {
54
- this.emit("ui_element_initialized", { id: this.id, role: this.themeRole });
55
- }
56
- dispose() {
57
- this.emit("ui_element_disposed", { id: this.id });
58
- }
59
- toJSON() {
60
- return {
61
- id: this.id,
62
- name: this.name,
63
- role: this.themeRole,
64
- variant: this.variant,
65
- size: this.size,
66
- disabled: this.isDisabled,
67
- metadata: this.metadata
68
- };
69
- }
70
- };
71
-
72
11
  // src/hierarchy/UIParent.tsx
73
12
  import React from "react";
74
- var UIModel = class extends UIElementBase {
75
- constructor(id, name, variant = "primary", size = "md") {
76
- super(id, name, "panel", variant, size);
77
- }
78
- };
79
13
  var UIParent = ({
80
14
  as: Component = "div",
81
15
  children,
@@ -86,7 +20,7 @@ var UIParent = ({
86
20
  };
87
21
 
88
22
  // src/hierarchy/ButtonParent.tsx
89
- import { useMemo } from "react";
23
+ import React2, { useMemo } from "react";
90
24
  import { jsx, jsxs } from "react/jsx-runtime";
91
25
  var SIZE_STYLES = {
92
26
  xs: "px-1.5 py-0.5 text-[9px] gap-1",
@@ -190,20 +124,50 @@ var ButtonParent = ({
190
124
  const effectiveLabel = label || tooltip || (typeof children === "string" ? children : void 0);
191
125
  const effectiveDesc = description || (shortcutHint ? `[${shortcutHint}]` : void 0);
192
126
  if (effectiveLabel || effectiveDesc) {
193
- setInspectionText({
194
- label: effectiveLabel,
195
- description: effectiveDesc
196
- });
127
+ setInspectionText(effectiveLabel || null, effectiveDesc || null);
197
128
  }
198
129
  };
199
130
  const handleMouseLeave = () => {
200
131
  setInspectionText(null);
201
132
  };
133
+ const store = useFloksFrnlStore();
134
+ const autoId = React2.useId().replace(/[:/]/g, "");
135
+ const effectiveId = id ?? (label ? `floks.button.${label.toLowerCase().replace(/[^a-z0-9]/g, "-")}.${autoId}` : `floks.button.${autoId}`);
136
+ const effectiveState = disabled ? "disabled" : isActive ? "active" : "default";
137
+ React2.useEffect(() => {
138
+ store.getState().registerEntity(effectiveId, {
139
+ state: effectiveState,
140
+ properties: { variant, size, disabled, isActive, label, kind: "primitive" }
141
+ });
142
+ return () => {
143
+ store.getState().unregisterEntity(effectiveId);
144
+ };
145
+ }, [effectiveId, store]);
146
+ React2.useEffect(() => {
147
+ const entity = store.getState().entities[effectiveId];
148
+ if (entity) {
149
+ if (entity.state !== effectiveState) {
150
+ store.getState().setEntityState(effectiveId, effectiveState);
151
+ }
152
+ store.getState().setEntityProperties(effectiveId, {
153
+ variant,
154
+ size,
155
+ disabled,
156
+ isActive,
157
+ label,
158
+ kind: "primitive"
159
+ });
160
+ }
161
+ }, [effectiveId, effectiveState, variant, size, disabled, isActive, label, store]);
202
162
  return /* @__PURE__ */ jsxs(
203
163
  "button",
204
164
  {
205
- id,
165
+ ...rest,
166
+ id: effectiveId,
206
167
  "data-ui-parent": "ButtonParent",
168
+ "data-frnl-id": effectiveId,
169
+ "data-frnl-kind": "ui-primitive",
170
+ "data-frnl-state": effectiveState,
207
171
  type: "button",
208
172
  disabled,
209
173
  onMouseEnter: handleMouseEnter,
@@ -214,9 +178,8 @@ var ButtonParent = ({
214
178
  if (disabled) return;
215
179
  onClick?.(e);
216
180
  },
217
- style: { ...variantStyle, ...activeDynamicStyle, ...style },
218
181
  className: `inline-flex items-center justify-center font-mono font-bold uppercase tracking-wider border transition-all duration-150 active:scale-95 select-none disabled:opacity-40 disabled:pointer-events-none ${SIZE_STYLES[size]} ${className}`,
219
- ...rest,
182
+ style: { ...variantStyle, ...activeDynamicStyle, ...style },
220
183
  children: [
221
184
  icon && /* @__PURE__ */ jsx("span", { className: "shrink-0", children: icon }),
222
185
  children && /* @__PURE__ */ jsx("span", { children })
@@ -224,10 +187,88 @@ var ButtonParent = ({
224
187
  }
225
188
  );
226
189
  };
227
- var ButtonModel = class extends UIElementBase {
228
- constructor(id, name, variant = "primary", size = "md") {
229
- super(id, name, "button", variant, size);
230
- }
190
+ var SplitButton = ({
191
+ id,
192
+ label,
193
+ onPrimaryClick,
194
+ onDropdownClick,
195
+ variant = "primary",
196
+ size = "md",
197
+ disabled = false,
198
+ isOpen = false,
199
+ dropdownMenu,
200
+ menuOccupants,
201
+ className = "",
202
+ style
203
+ }) => {
204
+ const store = useFloksFrnlStore();
205
+ const autoId = React2.useId().replace(/[:/]/g, "");
206
+ const compositeId = id ?? (label ? `floks.splitbutton.${label.toLowerCase().replace(/[^a-z0-9]/g, "-")}.${autoId}` : `floks.splitbutton.${autoId}`);
207
+ const effectiveState = disabled ? "disabled" : isOpen ? "open" : "default";
208
+ React2.useEffect(() => {
209
+ store.getState().registerEntity(compositeId, {
210
+ state: effectiveState,
211
+ properties: { variant, size, isOpen, label, kind: "composite" }
212
+ });
213
+ return () => {
214
+ store.getState().unregisterEntity(compositeId);
215
+ };
216
+ }, [compositeId, store]);
217
+ React2.useEffect(() => {
218
+ const entity = store.getState().entities[compositeId];
219
+ if (entity) {
220
+ if (entity.state !== effectiveState) {
221
+ store.getState().setEntityState(compositeId, effectiveState);
222
+ }
223
+ store.getState().setEntityProperties(compositeId, {
224
+ variant,
225
+ size,
226
+ isOpen,
227
+ label,
228
+ kind: "composite"
229
+ });
230
+ }
231
+ }, [compositeId, effectiveState, variant, size, isOpen, label, store]);
232
+ return /* @__PURE__ */ jsxs(
233
+ "div",
234
+ {
235
+ id: compositeId,
236
+ "data-frnl-id": compositeId,
237
+ "data-frnl-kind": "ui-composite",
238
+ "data-frnl-state": effectiveState,
239
+ className: `inline-flex items-center relative ${className}`,
240
+ style,
241
+ children: [
242
+ /* @__PURE__ */ jsx(
243
+ ButtonParent,
244
+ {
245
+ id: `${compositeId}.primaryAction`,
246
+ variant,
247
+ size,
248
+ disabled,
249
+ onClick: onPrimaryClick,
250
+ className: "rounded-r-none border-r-0",
251
+ children: label
252
+ }
253
+ ),
254
+ /* @__PURE__ */ jsx(
255
+ ButtonParent,
256
+ {
257
+ id: `${compositeId}.dropdownAction`,
258
+ variant,
259
+ size,
260
+ disabled,
261
+ onClick: onDropdownClick,
262
+ className: "rounded-l-none px-2",
263
+ "aria-label": "Toggle Menu",
264
+ "aria-expanded": isOpen,
265
+ children: "\u25BE"
266
+ }
267
+ ),
268
+ isOpen && (dropdownMenu || menuOccupants) && /* @__PURE__ */ jsx("div", { className: "absolute top-full left-0 mt-1 z-50", children: dropdownMenu || menuOccupants && Object.values(menuOccupants)[0] })
269
+ ]
270
+ }
271
+ );
231
272
  };
232
273
  var ActionButton = (props) => /* @__PURE__ */ jsx(ButtonParent, { variant: "accent", ...props });
233
274
  var IconButton = (props) => /* @__PURE__ */ jsx(ButtonParent, { variant: "secondary", ...props });
@@ -356,11 +397,6 @@ var PanelParent = ({
356
397
  }
357
398
  );
358
399
  };
359
- var PanelModel = class extends UIElementBase {
360
- constructor(id, name, variant = "primary", size = "md") {
361
- super(id, name, "panel", variant, size);
362
- }
363
- };
364
400
  var ModalPanel = (props) => /* @__PURE__ */ jsx2(PanelParent, { zIndex: 100, ...props });
365
401
  var InspectorPanel = (props) => /* @__PURE__ */ jsx2(PanelParent, { ...props });
366
402
  var CardPanel = (props) => /* @__PURE__ */ jsx2(PanelParent, { ...props });
@@ -502,11 +538,6 @@ var ToggleInput = ({
502
538
  }
503
539
  );
504
540
  };
505
- var InputModel = class extends UIElementBase {
506
- constructor(id, name, variant = "primary", size = "md") {
507
- super(id, name, "input", variant, size);
508
- }
509
- };
510
541
 
511
542
  // src/hierarchy/TileParent.tsx
512
543
  import { useState, useRef, useEffect } from "react";
@@ -597,11 +628,6 @@ var TileParent = ({
597
628
  }
598
629
  );
599
630
  };
600
- var TileModel = class extends UIElementBase {
601
- constructor(id, name, variant = "primary", size = "md") {
602
- super(id, name, "tile", variant, size);
603
- }
604
- };
605
631
  var SpawnOptionTile = (props) => /* @__PURE__ */ jsx4(TileParent, { ...props });
606
632
  var CategoryCardTile = (props) => /* @__PURE__ */ jsx4(TileParent, { ...props });
607
633
  var ClipCardTile = (props) => /* @__PURE__ */ jsx4(TileParent, { ...props });
@@ -727,26 +753,17 @@ var StatusDot = ({
727
753
  }
728
754
  );
729
755
  };
730
- var BadgeModel = class extends UIElementBase {
731
- constructor(id, name, variant = "primary", size = "md") {
732
- super(id, name, "badge", variant, size);
733
- }
734
- };
735
756
 
736
757
  export {
737
- registerPaletteResolver,
738
- UIElementBase,
739
- UIModel,
740
758
  UIParent,
741
759
  ButtonParent,
742
- ButtonModel,
760
+ SplitButton,
743
761
  ActionButton,
744
762
  IconButton,
745
763
  ToggleButton,
746
764
  DangerButton,
747
765
  GhostButton,
748
766
  PanelParent,
749
- PanelModel,
750
767
  ModalPanel,
751
768
  InspectorPanel,
752
769
  CardPanel,
@@ -755,15 +772,12 @@ export {
755
772
  TextInput,
756
773
  SelectInput,
757
774
  ToggleInput,
758
- InputModel,
759
775
  TileParent,
760
- TileModel,
761
776
  SpawnOptionTile,
762
777
  CategoryCardTile,
763
778
  ClipCardTile,
764
779
  ChannelStripTile,
765
780
  Badge,
766
781
  TelemetryPill,
767
- StatusDot,
768
- BadgeModel
782
+ StatusDot
769
783
  };
@@ -0,0 +1,22 @@
1
+ // src/frnl/store.ts
2
+ import React, { createContext, useContext } from "react";
3
+ import { createFrnlStore } from "@ultrst-armada/frnlts";
4
+ var floksFrnlStore = createFrnlStore();
5
+ function getFloksFrnlStore() {
6
+ return floksFrnlStore;
7
+ }
8
+ var FrnlStoreContext = createContext(null);
9
+ var FrnlStoreProvider = ({ store, children }) => {
10
+ return React.createElement(FrnlStoreContext.Provider, { value: store }, children);
11
+ };
12
+ function useFloksFrnlStore() {
13
+ const contextStore = useContext(FrnlStoreContext);
14
+ return contextStore ?? floksFrnlStore;
15
+ }
16
+
17
+ export {
18
+ floksFrnlStore,
19
+ getFloksFrnlStore,
20
+ FrnlStoreProvider,
21
+ useFloksFrnlStore
22
+ };