block / developer-tools
PREVIEW READYSynchronized Workbench
Paired text workspaces that resize together through pointer and keyboard controls.
import { Button } from "@/components/designos/button"
export function Interface() {
return <Button>Initialize</Button>
}INSTALL / SOURCE REGISTRY
Add Synchronized Workbench to your project
The command adds the component source and its DesignOS base dependency.
$
pnpm dlx shadcn@latest add https://designos.h4rithd.com/r/synchronized-workbench.jsonregistry/default/blocks/synchronized-workbench.tsx
"use client"
import * as React from "react"
import "@/components/designos/designos.css"
export function SynchronizedWorkbench({ input, output, onInputChange, minHeight = 240, maxHeight = 760 }: { input: string; output: string; onInputChange: (value: string) => void; minHeight?: number; maxHeight?: number }) {
const [height, setHeight] = React.useState(360)
const start = React.useRef({ y: 0, height: 360 })
const clamp = React.useCallback((value: number) => Math.min(maxHeight, Math.max(minHeight, value)), [maxHeight, minHeight])
const begin = (event: React.PointerEvent<HTMLButtonElement>) => {
event.currentTarget.setPointerCapture(event.pointerId)
start.current = { y: event.clientY, height }
}
return <div className="dos-workbench" style={{ "--dos-workbench-height": `${height}px` } as React.CSSProperties}>
<section className="dos-workbench__pane"><header className="dos-workbench__head"><span>Input</span><span>{input.length} chars</span></header><textarea aria-label="Input" value={input} onChange={(event) => onInputChange(event.target.value)} placeholder="Enter text…" /><footer className="dos-workbench__meta">Resizable · synchronized</footer></section>
<section className="dos-workbench__pane"><header className="dos-workbench__head"><span>Output</span><span>{output.length} chars</span></header><textarea aria-label="Output" value={output} readOnly /><footer className="dos-workbench__meta">Read only</footer></section>
<button type="button" className="dos-workbench__resize" role="separator" aria-label="Resize both workbench panes" aria-orientation="horizontal" aria-valuemin={minHeight} aria-valuemax={maxHeight} aria-valuenow={height} onPointerDown={begin} onPointerMove={(event) => { if (event.currentTarget.hasPointerCapture(event.pointerId)) setHeight(clamp(start.current.height + event.clientY - start.current.y)) }} onKeyDown={(event) => { const amount = event.shiftKey ? 48 : 16; if (event.key === "ArrowUp") { event.preventDefault(); setHeight((value) => clamp(value - amount)) } if (event.key === "ArrowDown") { event.preventDefault(); setHeight((value) => clamp(value + amount)) } if (event.key === "Home") { event.preventDefault(); setHeight(minHeight) } if (event.key === "End") { event.preventDefault(); setHeight(maxHeight) } }} />
</div>
}
Component contract
- Source-owned after installation
- Dark and light theme variables
- Keyboard and visible-focus behavior
- Reduced-motion fallback
- Responsive from 320px upward
- IBM Plex Sans + JetBrains Mono hierarchy