Skip to content

Releases: remorses/gpuix

@gpuix/react@0.4.0

Choose a tag to compare

@remorses remorses released this 23 Aug 15:10

Also ships @gpuix/native@0.4.0. CI publishes both packages.

  1. Native motion.div animations — animate from an initial style to a target style. React sends the targets once. Rust interpolates the presentation style and requests GPUI frames. The React tree is not reconciled on each frame.

    import { motion } from '@gpuix/react'
    
    <motion.div
      initial={{ width: 0, opacity: 0 }}
      animate={{ width: 260, opacity: 1 }}
      transition={{ duration: 0.2, ease: 'easeOut' }}
    >
      Sidebar content
    </motion.div>

    Numeric targets: width, height, top, right, bottom, left, opacity, borderRadius. Timing uses seconds. ease is "linear", "ease", "easeIn", "easeOut", "easeInOut", or a cubic-bezier [x1, y1, x2, y2].

    Set initial={false} to mount at the first animate target. A running animation can reverse or change target without a jump.

  2. Playwright-like automation API — mark elements with testId, then drive them from tests or from another process.

    import { connectTest } from '@gpuix/react/automation'
    
    const app = await connectTest(renderer)
    await app.getByTestId('inc').click()
    await app.getByText('Count: 1').waitFor()
    await app.getByTestId('composer').fill('hello gpuix')
    await app.getByTestId('composer').press('enter')
    await app.captureFrames('review/sidebar', [0, 150, 300])

    Locators: getByTestId, getByText, getByType. app.clock.pause(), set(ms), and fastForward(ms) freeze native motion time.

    A live app listens on stdin when stdin is a pipe. launch({ command, args }) pipes stdin and speaks SSE data: lines:

    import { launch } from '@gpuix/react/automation'
    
    const app = await launch({ command: 'bun', args: ['examples/chat.tsx'] })
    await app.getByTestId('composer').fill('hello')
    await app.screenshot({ path: 'live.png' })
    await app.close()

@gpuix/react@0.3.0

Choose a tag to compare

@remorses remorses released this 23 Aug 13:33

Also ships @gpuix/native@0.3.0. CI publishes both packages.

  1. CSS grid on divdisplay: "grid" plus gridTemplateColumns maps to GPUI's Taffy grid. Use gridColumnMin: "max-content" for tables so each column is as wide as its widest cell.

    <div
      style={{
        display: 'grid',
        gridTemplateColumns: 3,
        gridColumnMin: 'max-content',
        rowGap: 1,
        columnGap: 1,
      }}
    >
      {cells}
    </div>

    gridTemplateRows and gridRowMin work the same on the other axis.

  2. Window chrome at open timerender() now honors a transparent titlebar, traffic-light position, and a blurred or transparent window background.

    import { render } from '@gpuix/react'
    
    render(<App />, {
      title: 'Waku',
      width: 1180,
      height: 820,
      titlebarTransparent: true,
      windowBackground: 'blurred',
      trafficLightX: 16,
      trafficLightY: 17,
    })

    windowBackground is "opaque" (default), "transparent", or "blurred".

  3. <diff> flows with its parent — it no longer owns a scroller unless you pass scroll. Use maxLines to keep a long patch short. Show more fires onShowMore.

    const [open, setOpen] = useState(false)
    
    <diff
      patch={unifiedPatch}
      wordDiff
      maxLines={open ? undefined : 24}
      onShowMore={() => setOpen(true)}
    />
  4. Debug frame overlay — see draw time on a live window.

    render(<App />, { title: 'My App', debugFrameOverlay: 'full' })

    Modes are hidden, minimal, and full. The readout is draw time, not FPS. 8.3 MS is about 120 Hz.

  5. Quit when the last window closes — the red traffic-light button now exits the process.

  6. Overlays block hits, and pointerEvents works — set pointerEvents: "none" to opt out. Set pointerEvents: "auto" to block even with no fill.

  7. Opaque Select, Combobox, and Tooltip surfacesFloatingLayer defaults to backgroundColor: "#1A1A1A".

  8. <svg> icons paint on the first frame — file paths and data:image/svg+xml URLs both work.

  9. bun --hot remounts no longer paint a black window.

  10. Cmd+Delete and Cmd+Backspace in <input> and <textarea> match the macOS system text field.

  11. Vertical wheel over overflowX: "scroll" stays on the parent. Trackpad X still pans the wide child.

  12. Parent scroller takes the wheel over a filled in-flow div.

  13. macOS scroll stays at the display rate on expensive frames.

  14. Faster first React mountapplyBatch sends styles and custom props as JSON values instead of double-encoded strings.

  15. Raw custom-prop values stay intact<anchored side="top"> commits again.

@gpuix/react@0.2.0

Choose a tag to compare

@remorses remorses released this 23 Aug 08:59

Also ships @gpuix/native@0.2.0. CI publishes both packages.

  1. Selectable text everywhere, plus <code>, <diff> and <markdown> — every string GPUIX paints can be selected with a drag and copied with Cmd+C. A drag can start in a plain <text> and end inside a code block.

    <div style={{ display: 'flex', flexDirection: 'column' }}>
      <text>drag from here</text>
      <code code={'and into this code block'} language="ts" />
    </div>

    Opt out with userSelect: 'none'. Read the selection with renderer.getSelectedText().

    <code code={source} language="typescript" showLineNumbers />
    <diff patch={unifiedPatch} wordDiff collapsedPaths={['pnpm-lock.yaml']} />
    <markdown source={readme} onLinkClick={(e) => open(e.value)} />

    Bundled languages: Rust, TypeScript, TSX, JavaScript, JSX, Python, Go, JSON, Bash, TOML, YAML, Markdown, HTML, CSS, C.

  2. Native <input> and <textarea> — caret, mouse selection, IME, clipboard, undo/redo, and grapheme-safe deletion. Enter submits. Shift+Enter inserts a newline in a textarea.

    <textarea value={draft} minRows={1} maxRows={8} onChange={(e) => setDraft(e.value ?? '')} onSubmit={send} />
  3. render() remounts React on the same native windowbun --hot saves remount the tree without a second window.

    import { render } from '@gpuix/react'
    render(<App />, { title: 'My App', width: 800, height: 600 })
  4. Headless Select, Combobox, and Tooltip — unstyled primitives. Import @gpuix/react/select, @gpuix/react/combobox, or @gpuix/react/tooltip and wrap them in local components/ui files.

    import * as SelectPrimitive from '@gpuix/react/select'
    
    <SelectPrimitive.Root value={model} onValueChange={setModel}>
      <SelectPrimitive.Trigger>
        <SelectPrimitive.Value placeholder="Select a model" />
      </SelectPrimitive.Trigger>
      <SelectPrimitive.Content>
        <SelectPrimitive.Item value="sonnet">Sonnet</SelectPrimitive.Item>
      </SelectPrimitive.Content>
    </SelectPrimitive.Root>
  5. <virtual-list> — GPUI builds only rows near the viewport.

    <virtual-list alignment="bottom" followTail estimatedItemHeight={180}>
      {messages.map((message) => <Message key={message.id} message={message} />)}
    </virtual-list>
  6. Tintable local SVG icons

    <svg src="/absolute/path/to/search.svg" style={{ width: 16, height: 16, color: '#b4b4b4' }} />
  7. startFrameLoop() — idle CPU drops from ~73% to ~1%. Default pace is ~125fps.

    import { startFrameLoop } from '@gpuix/react'
    startFrameLoop(renderer)
  8. Native GPUI platform on macOS, Windows, and Linux. Windows runtime validation is still pending.

  9. GPUI upgrade to zed d5dc01f2. Scroll events can report touchPhase: 'cancelled'. Building from source now needs Rust 1.97.1 and the Metal toolchain on macOS.

  10. Style props that were declared and dropped now work — full styles on <text>, plus fontSize, textAlign, rowGap, columnGap, lineHeight, and borderWidth: 0.

  11. autoFocus works and <input> no longer ships a hardcoded look.

  12. Blinking caret every 500ms while focused. Colour is theme.caret.

  13. Clipboard and natural scroll — Cmd+C writes to the system clipboard. Wheel deltas keep the OS sign.

  14. React 19 JSX components can return any ReactNode.