Text area
Interactive: type, watch the counter
States: disabled, read-only
// Web: React, consuming @dizyx/nockerl-tokens. (Web is the laggard platform; this is the// canonical API the published @dizyx/nockerl-react package exposes. The shipped shadcn// textarea.tsx is being conformed to it; see the drift note below.)import { NockerlTextArea } from '@dizyx/nockerl-react';
export function TaskPrompt() { const [prompt, setPrompt] = useState(''); return ( <NockerlTextArea label="Task instructions" value={prompt} onChange={setPrompt} placeholder="Describe what the agent should do…" helperText="The agent runs with these instructions." maxLength={2000} minRows={3} maxRows={12} /> );}// Android (Jetpack Compose, canonical). There is no extracted NockerlTextArea yet; a// multi-line field is the same OutlinedTextField WITHOUT singleLine, with a maxLines cap// (see com.nockerl.app.chat.ui.ChatInputBar, which caps at 5 lines). Flagged in drift.import androidx.compose.material3.OutlinedTextFieldimport androidx.compose.material3.OutlinedTextFieldDefaultsimport com.nockerl.app.core.theme.NockerlControlShape
OutlinedTextField( value = prompt, onValueChange = { if (it.length <= MAX) prompt = it }, label = { Text("Task instructions") }, maxLines = 12, // auto-grows up to this, then scrolls isError = prompt.length > MAX, supportingText = { Text("${prompt.length} / $MAX") // the character counter }, modifier = Modifier.fillMaxWidth(), shape = NockerlControlShape, // the 12dp control radius token colors = OutlinedTextFieldDefaults.colors( focusedBorderColor = MaterialTheme.colorScheme.primary, // brand cyan ),)// macOS: SwiftUI (canonical). A multi-line field is a TextField with a vertical axis// (macOS 13+) wrapped in the same recessed .nockerlFieldBackground() modifier from// NockerlVoice/UI/SettingsComponents.swift. The counter is a sibling Text view.TextField("Describe what the agent should do…", text: $prompt, axis: .vertical) .textFieldStyle(.plain) .lineLimit(3...12) // grows between 3 and 12 lines, then scrolls .foregroundStyle(NockerlTheme.onSurface) .nockerlFieldBackground() // canvasAlt inset surface + hairline, rounded rect
Text("\(prompt.count) / \(maxLen)") // character counter (sibling) .font(.caption) .foregroundStyle(prompt.count > maxLen ? NockerlTheme.error : NockerlTheme.onSurfaceMuted)Parameters
Section titled “Parameters”| Prop | Type | Default | Description |
|---|---|---|---|
label * | string | Persistent label, bound to the field via htmlFor / id. Never a placeholder. | |
value * | string | Current value (controlled). | |
onChange | (value: string) => void | Change handler. Ignored while disabled or read-only. | |
placeholder | string | Ghost prompt INSIDE the well. It is supplementary, never the label. | |
helperText | string | Helper text under the well. Replaced by errorText / statusText when set. | |
errorText | string | When set, the area renders its error treatment (red border + this message). | |
status | FieldStatus | The validation ladder: warning / success (or error) border + a matching, icon-carrying help line showing statusText. errorText (and an over-cap counter) still win if set. Color is never the only signal. A glyph rides along. | |
statusText | string | The message shown for status (ignored when errorText / over-cap wins). | |
required | boolean | false | Marks the field required: renders the status-colored * after the label. |
disabled | boolean | false | Inert + clearly-seen (never invisible). |
readOnly | boolean | false | Value shown, selectable, not editable. |
maxLength | number | Optional max length; drives the character counter + over-cap error. | |
minRows | number | 3 | Rows the field starts at before it auto-grows. |
maxRows | number | 8 | Rows it grows to before it starts scrolling instead. |
Also accepts the native <textarea> attributes (e.g. onClick, disabled, id, aria-*, data-*), forwarded straight through, plus a forwarded ref.
Compose has no extracted NockerlTextArea yet; a multi-line field is the same
OutlinedTextField without singleLine, with these parameters. The recessed-well
treatment and the counter are not built in (M3 outlined boxes are used; the counter is
a manual supportingText); tracked in drift below.
| Parameter | Type | Default | Description |
|---|---|---|---|
value * | String | Current text value. | |
onValueChange * | (String) -> Unit | Invoked on every edit. Enforce a length cap here (no built-in maxLength). | |
label | @Composable (() -> Unit)? | null | Floating label slot: the persistent label. |
maxLines | Int | Int.MAX_VALUE | The area auto-grows up to this many lines, then scrolls. (Leave singleLine off.) |
minLines | Int | 1 | Starting height in lines. |
isError | Boolean | false | Renders the error treatment (red outline + error-colored label/supporting text). |
supportingText | @Composable (() -> Unit)? | null | Helper / error line, and where the current / max counter is rendered. |
shape | Shape | NockerlControlShape | Pass the NockerlControlShape token (12dp) so the area shares the one control radius. |
colors | TextFieldColors | OutlinedTextFieldDefaults.colors(…) | Set focusedBorderColor = colorScheme.primary for the brand-cyan focus edge. |
SwiftUI applies the area chrome as the .nockerlFieldBackground() view modifier on a
vertical-axis TextField. State (focus, editing) comes from the SwiftUI environment.
There are no explicit state parameters, and the label, counter, and error are sibling
views (no bundled slots yet).
| Style | Type | Default | Description |
|---|---|---|---|
.nockerlFieldBackground() | ViewModifier | The recessed input chrome: NockerlTheme.canvasAlt inset surface + a hairline stroke, in a rounded rectangle. Wrap a .textFieldStyle(.plain) field with it. | |
axis: .vertical | TextField init arg | Makes the TextField multi-line (macOS 13+) so it can grow. | |
.lineLimit(min...max) | modifier | Auto-grow bounds: grows between the two line counts, then scrolls. | |
.foregroundStyle(NockerlTheme.onSurface) | modifier | Binds the typed-text color to the on-surface token. |