@vsreact/core 0.0.21 → 0.0.23

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.
@@ -21,6 +21,7 @@ const eventPropNames = {
21
21
  onMouseUp: "mouseup",
22
22
  onMouseMove: "mousemove",
23
23
  onKeyDown: "keydown",
24
+ onKeyUp: "keyup",
24
25
  onDragStart: "dragstart",
25
26
  onDrag: "drag",
26
27
  onDragEnd: "dragend",
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import "./runtime";
2
2
  import "./bridge";
3
3
  /** The SDK version — stamp it on support dumps and analytics. */
4
- export declare const VERSION = "0.0.21";
4
+ export declare const VERSION = "0.0.23";
5
5
  export { View, Text, Image, TextInput, NativeView } from "./primitives";
6
6
  export type { CommonProps, TextProps, ImageProps, TextInputProps, NativeViewProps, } from "./primitives";
7
7
  export { render, unmount } from "./render";
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  import "./runtime";
4
4
  import "./bridge";
5
5
  /** The SDK version — stamp it on support dumps and analytics. */
6
- export const VERSION = "0.0.21";
6
+ export const VERSION = "0.0.23";
7
7
  export { View, Text, Image, TextInput, NativeView } from "./primitives";
8
8
  export { render, unmount } from "./render";
9
9
  export { native } from "./native";
@@ -56,6 +56,8 @@ export interface CommonProps {
56
56
  makes the node focusable: click focuses it, Tab cycles, keys arrive
57
57
  here with web KeyboardEvent.key names. */
58
58
  onKeyDown?: (e: KeyEventPayload) => void;
59
+ /** Fires when a key seen by onKeyDown is released (node still focused). */
60
+ onKeyUp?: (e: KeyEventPayload) => void;
59
61
  onFocus?: () => void;
60
62
  onBlur?: () => void;
61
63
  /** Fires after layout whenever this node's root-space rect changes —
@@ -105,6 +107,7 @@ export declare function TextInput({ onChange, onSubmit, ...rest }: TextInputProp
105
107
  onDragEnd?: ((e: DragEventPayload) => void) | undefined;
106
108
  onMouseMove?: ((e: MouseMovePayload) => void) | undefined;
107
109
  onKeyDown?: ((e: KeyEventPayload) => void) | undefined;
110
+ onKeyUp?: ((e: KeyEventPayload) => void) | undefined;
108
111
  onLayout?: ((rect: LayoutRect) => void) | undefined;
109
112
  }, string | import("react").JSXElementConstructor<any>>;
110
113
  export interface NativeViewProps extends CommonProps {
package/dist/tw.js CHANGED
@@ -51,6 +51,14 @@ const staticClasses = {
51
51
  "font-medium": { fontWeight: 500 },
52
52
  "font-semibold": { fontWeight: 600 },
53
53
  "font-bold": { fontWeight: 700 },
54
+ truncate: { numberOfLines: 1 },
55
+ uppercase: { textTransform: "uppercase" },
56
+ lowercase: { textTransform: "lowercase" },
57
+ capitalize: { textTransform: "capitalize" },
58
+ "normal-case": { textTransform: "none" },
59
+ underline: { textDecoration: "underline" },
60
+ "line-through": { textDecoration: "line-through" },
61
+ "no-underline": { textDecoration: "none" },
54
62
  "tracking-tighter": { letterSpacing: -0.8 },
55
63
  "tracking-tight": { letterSpacing: -0.4 },
56
64
  "tracking-normal": { letterSpacing: 0 },
@@ -63,6 +71,21 @@ const staticClasses = {
63
71
  "cursor-pointer": { cursor: "pointer" },
64
72
  "cursor-text": { cursor: "text" },
65
73
  "cursor-default": { cursor: "default" },
74
+ "cursor-grab": { cursor: "grab" },
75
+ "cursor-grabbing": { cursor: "grabbing" },
76
+ "cursor-move": { cursor: "move" },
77
+ "cursor-ns-resize": { cursor: "ns-resize" },
78
+ "cursor-ew-resize": { cursor: "ew-resize" },
79
+ "cursor-crosshair": { cursor: "crosshair" },
80
+ "cursor-not-allowed": { cursor: "not-allowed" },
81
+ "pointer-events-none": { pointerEvents: "none" },
82
+ "pointer-events-auto": { pointerEvents: "auto" },
83
+ "border-solid": { borderStyle: "solid" },
84
+ "border-dashed": { borderStyle: "dashed" },
85
+ "border-dotted": { borderStyle: "dotted" },
86
+ "object-contain": { objectFit: "contain" },
87
+ "object-cover": { objectFit: "cover" },
88
+ "object-fill": { objectFit: "fill" },
66
89
  shadow: { shadowColor: "#00000066", shadowRadius: 4, shadowOffsetY: 1 },
67
90
  "shadow-sm": { shadowColor: "#00000066", shadowRadius: 4, shadowOffsetY: 1 },
68
91
  "shadow-md": { shadowColor: "#00000066", shadowRadius: 8, shadowOffsetY: 2 },
@@ -287,9 +310,21 @@ function resolveClass(cls) {
287
310
  return Number.isFinite(n) ? { opacity: n / 100 } : undefined;
288
311
  }
289
312
  case "leading": {
313
+ if (rest.startsWith("[")) {
314
+ const px = parseLength(rest);
315
+ return typeof px === "number" ? { lineHeight: px } : undefined;
316
+ }
290
317
  const n = Number(rest);
291
318
  return Number.isFinite(n) ? { lineHeight: n * 4 } : undefined;
292
319
  }
320
+ case "line": {
321
+ // line-clamp-N (rest arrives as "clamp-N")
322
+ if (rest.startsWith("clamp-")) {
323
+ const n = Number(rest.slice("clamp-".length));
324
+ return Number.isFinite(n) && n > 0 ? { numberOfLines: n } : undefined;
325
+ }
326
+ return undefined;
327
+ }
293
328
  case "tracking": {
294
329
  // Named scale lives in staticClasses; arbitrary is px: tracking-[3].
295
330
  if (rest.startsWith("[")) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vsreact/core",
3
- "version": "0.0.21",
3
+ "version": "0.0.23",
4
4
  "description": "Write React. Ship native VST. A React renderer for JUCE audio plugins — QuickJS + Yoga + juce::Graphics, no webview.",
5
5
  "keywords": [
6
6
  "react",
@@ -820,3 +820,18 @@ describe("keyboard control model (0.0.20)", () => {
820
820
  expect(seen.at(-1)?.[1]).toBeCloseTo(0.49);
821
821
  });
822
822
  });
823
+
824
+ describe("keyup wiring (0.0.22)", () => {
825
+ test("onKeyUp registers the keyup listener", () => {
826
+ const ups: string[] = [];
827
+ render(<Slider value={0.5} onChange={() => {}} />);
828
+ unmount();
829
+ batches.length = 0;
830
+ const { View: V } = require("./index");
831
+ render(<V onKeyUp={(e: any) => ups.push(e.key)} />);
832
+
833
+ const id = nodeWithListener("keyup");
834
+ dispatch({ kind: "event", nodeId: id, type: "keyup", payload: { key: "ArrowUp", shift: false, ctrl: false, alt: false, meta: false } });
835
+ expect(ups).toEqual(["ArrowUp"]);
836
+ });
837
+ });
package/src/hostConfig.ts CHANGED
@@ -32,6 +32,7 @@ const eventPropNames: Record<string, string> = {
32
32
  onMouseUp: "mouseup",
33
33
  onMouseMove: "mousemove",
34
34
  onKeyDown: "keydown",
35
+ onKeyUp: "keyup",
35
36
  onDragStart: "dragstart",
36
37
  onDrag: "drag",
37
38
  onDragEnd: "dragend",
package/src/index.ts CHANGED
@@ -4,7 +4,7 @@ import "./runtime";
4
4
  import "./bridge";
5
5
 
6
6
  /** The SDK version — stamp it on support dumps and analytics. */
7
- export const VERSION = "0.0.21";
7
+ export const VERSION = "0.0.23";
8
8
 
9
9
  export { View, Text, Image, TextInput, NativeView } from "./primitives";
10
10
  export type {
@@ -62,6 +62,8 @@ export interface CommonProps {
62
62
  makes the node focusable: click focuses it, Tab cycles, keys arrive
63
63
  here with web KeyboardEvent.key names. */
64
64
  onKeyDown?: (e: KeyEventPayload) => void;
65
+ /** Fires when a key seen by onKeyDown is released (node still focused). */
66
+ onKeyUp?: (e: KeyEventPayload) => void;
65
67
  onFocus?: () => void;
66
68
  onBlur?: () => void;
67
69
  /** Fires after layout whenever this node's root-space rect changes —
@@ -83,6 +85,8 @@ export function Text(props: TextProps) {
83
85
 
84
86
  export interface ImageProps extends CommonProps {
85
87
  src: string;
88
+ /** Style keys also work: objectFit ("contain" default | "cover" | "fill")
89
+ and tintColor (fills the image's alpha with a solid colour). */
86
90
  }
87
91
 
88
92
  export function Image(props: ImageProps) {
package/src/tw.test.ts CHANGED
@@ -234,3 +234,31 @@ describe("zIndex (0.0.20)", () => {
234
234
  expect(tw("-z-1").style).toEqual({ zIndex: -1 });
235
235
  });
236
236
  });
237
+
238
+ describe("typography (0.0.22)", () => {
239
+ test("truncate and line-clamp", () => {
240
+ expect(tw("truncate").style).toEqual({ numberOfLines: 1 });
241
+ expect(tw("line-clamp-3").style).toEqual({ numberOfLines: 3 });
242
+ });
243
+
244
+ test("case transforms and decorations", () => {
245
+ expect(tw("uppercase").style).toEqual({ textTransform: "uppercase" });
246
+ expect(tw("capitalize").style).toEqual({ textTransform: "capitalize" });
247
+ expect(tw("underline").style).toEqual({ textDecoration: "underline" });
248
+ expect(tw("line-through").style).toEqual({ textDecoration: "line-through" });
249
+ });
250
+
251
+ test("arbitrary leading is px", () => {
252
+ expect(tw("leading-[18]").style).toEqual({ lineHeight: 18 });
253
+ expect(tw("leading-5").style).toEqual({ lineHeight: 20 });
254
+ });
255
+ });
256
+
257
+ describe("input & media classes (0.0.23)", () => {
258
+ test("pointer events, border styles, object fit", () => {
259
+ expect(tw("pointer-events-none").style).toEqual({ pointerEvents: "none" });
260
+ expect(tw("border-dashed").style).toEqual({ borderStyle: "dashed" });
261
+ expect(tw("object-cover").style).toEqual({ objectFit: "cover" });
262
+ expect(tw("cursor-grab").style).toEqual({ cursor: "grab" });
263
+ });
264
+ });
package/src/tw.ts CHANGED
@@ -62,6 +62,14 @@ const staticClasses: Record<string, Style> = {
62
62
  "font-medium": { fontWeight: 500 },
63
63
  "font-semibold": { fontWeight: 600 },
64
64
  "font-bold": { fontWeight: 700 },
65
+ truncate: { numberOfLines: 1 },
66
+ uppercase: { textTransform: "uppercase" },
67
+ lowercase: { textTransform: "lowercase" },
68
+ capitalize: { textTransform: "capitalize" },
69
+ "normal-case": { textTransform: "none" },
70
+ underline: { textDecoration: "underline" },
71
+ "line-through": { textDecoration: "line-through" },
72
+ "no-underline": { textDecoration: "none" },
65
73
  "tracking-tighter": { letterSpacing: -0.8 },
66
74
  "tracking-tight": { letterSpacing: -0.4 },
67
75
  "tracking-normal": { letterSpacing: 0 },
@@ -74,6 +82,21 @@ const staticClasses: Record<string, Style> = {
74
82
  "cursor-pointer": { cursor: "pointer" },
75
83
  "cursor-text": { cursor: "text" },
76
84
  "cursor-default": { cursor: "default" },
85
+ "cursor-grab": { cursor: "grab" },
86
+ "cursor-grabbing": { cursor: "grabbing" },
87
+ "cursor-move": { cursor: "move" },
88
+ "cursor-ns-resize": { cursor: "ns-resize" },
89
+ "cursor-ew-resize": { cursor: "ew-resize" },
90
+ "cursor-crosshair": { cursor: "crosshair" },
91
+ "cursor-not-allowed": { cursor: "not-allowed" },
92
+ "pointer-events-none": { pointerEvents: "none" },
93
+ "pointer-events-auto": { pointerEvents: "auto" },
94
+ "border-solid": { borderStyle: "solid" },
95
+ "border-dashed": { borderStyle: "dashed" },
96
+ "border-dotted": { borderStyle: "dotted" },
97
+ "object-contain": { objectFit: "contain" },
98
+ "object-cover": { objectFit: "cover" },
99
+ "object-fill": { objectFit: "fill" },
77
100
  shadow: { shadowColor: "#00000066", shadowRadius: 4, shadowOffsetY: 1 },
78
101
  "shadow-sm": { shadowColor: "#00000066", shadowRadius: 4, shadowOffsetY: 1 },
79
102
  "shadow-md": { shadowColor: "#00000066", shadowRadius: 8, shadowOffsetY: 2 },
@@ -295,9 +318,21 @@ function resolveClass(cls: string): Style | undefined {
295
318
  return Number.isFinite(n) ? { opacity: n / 100 } : undefined;
296
319
  }
297
320
  case "leading": {
321
+ if (rest.startsWith("[")) {
322
+ const px = parseLength(rest);
323
+ return typeof px === "number" ? { lineHeight: px } : undefined;
324
+ }
298
325
  const n = Number(rest);
299
326
  return Number.isFinite(n) ? { lineHeight: n * 4 } : undefined;
300
327
  }
328
+ case "line": {
329
+ // line-clamp-N (rest arrives as "clamp-N")
330
+ if (rest.startsWith("clamp-")) {
331
+ const n = Number(rest.slice("clamp-".length));
332
+ return Number.isFinite(n) && n > 0 ? { numberOfLines: n } : undefined;
333
+ }
334
+ return undefined;
335
+ }
301
336
  case "tracking": {
302
337
  // Named scale lives in staticClasses; arbitrary is px: tracking-[3].
303
338
  if (rest.startsWith("[")) {