Skip to main content

noir 0.0.6 copy "noir: ^0.0.6" to clipboard
noir: ^0.0.6 copied to clipboard

A Flutter-like reactive terminal UI framework for Dart, powered by OpenTUI.

Changelog #

0.0.6 #

Corrects lifecycle, text geometry, input, image capture, and hot reload. The native ABI and bundled native artifacts are unchanged from 0.0.5. This remains an early 0.0.x release, not a 1.0 claim.

Fixed #

  • Keyed focus traversal follows the child order from the latest reconciliation. After a keyed reorder, Tab and Shift+Tab move through that order. A focus callback can reattach its node during detach, redirect primary focus, dispose the node that just gained focus, or dispose the focused child and choose another, and that decision is the one that sticks. An explicit empty-focus decision during disabling cancels recovery.
  • A render object that would adopt itself or an ancestor throws StateError and leaves the existing edges in place. Detach still visits every sibling and descendant when one of them throws, reports the first error, and drops them from the pipeline. A cleanup failure still invalidates after the attempt.
  • Spinner rejects a non-positive interval with ArgumentError before it starts. An empty frames list still throws, as it did in 0.0.5. The message says the interval must be positive.
  • AnimationController.forward and reverse reject a from value that is not finite with ArgumentError, and leave an in-flight run and its Future intact. A listener that stops, resets, disposes, or replaces the run during the start notification ends only that run: a replacement keeps its own Future, and disposal cannot restart the ticker.
  • TextSpan segments graphemes across the whole span tree. A grapheme that crosses span boundaries takes its style and hyperlink from the span where it starts. CodeView, TextTable, and document lines keep UTF-16 ranges for CRLF rows and for empty rows. TextEditingController treats CRLF as one hard break and leaves a standalone CR in the line.
  • TextInput and TextArea keep the caret on the painted cell for wrapped and CRLF text, including after a resize of an exact-height unwrapped TextArea. A zero-width TextInput publishes no caret.
  • Select snapshots its options, so a later edit of the same list is a real change. The highlight stays inside the list when that list shrinks, even if selectedIndex itself did not change. Paint and pointer hits use the scroll window from the latest highlight, so Home, End, and a click agree with the painted rows. TabSelect clamps its selection the same way when the options list changes without a new selectedIndex.
  • DiffView keeps custom-row layout and hunk measurements across a rebuild that does not change row geometry, and replaces them when a custom row height does change.
  • Canceling an input registration during dispatch skips that registration and still runs the ones that remain, in their original order. App exit cancels later app handlers in the same dispatch. Legacy ESC-prefixed Alt keys, SS3 application-cursor keys, and CSI function keys stay one key across split reads. A bare Escape still flushes without an Alt modifier.
  • Nested Buffer.clipped views intersect their rectangles, so a child view cannot widen its parent's clip. drawFrameBuffer intersects a clipped view's copy rectangle with that clip and the buffer bounds before the native call, preserving source offsets.
  • The testing renderer records an image's source pixels as colored block cells, including a source crop and a clip that crops those pixels with the destination. BufferCapture keeps those pixels across repeated captures, and reports the primary capture error when cleanup also fails. An HTTP image load spends one timeout across the request, the response, and the body. A stalled body is cancelled off that timeout's critical path.
  • Hot reload notices a same-size edit that preserves the modification time. A source that disappears between listing and reading is absent for that scan and can return on a later one. A directory listing that fails is retried on a later poll. An unrelated read error propagates and stops hot reload for that process.

Changed #

  • Low-level Buffer.drawFrameBuffer copies can still write or clear a wide grapheme across a clip edge. That limit is documented on the platform limitations page. The bundled native artifacts are unchanged from 0.0.5.

0.0.5 #

Adds a public clipboard request, fixes text alignment and cell painting, character keys, text editing, and hot-reload shutdown, and removes unused prerelease API. The native ABI and bundled native artifacts are unchanged from 0.0.4.

Added #

  • TuiApp.copyToClipboard(text) asks the terminal to copy text to the system clipboard with an OSC 52 escape sequence. It returns whether the sequence was written, not whether the terminal honored it, and returns false without writing for empty text or a headless app. Before this the OSC 52 path was reachable only through the internal BuildOwner and the low-level Renderer extension.

Fixed #

  • Text and RichText place each line inside their box for TextAlign.center and TextAlign.right. Every line used to share the offset of the widest line, so shorter wrapped lines stayed at the left, and a line wider than the box kept its leading cells whatever the alignment. An over-wide line now keeps its trailing cells when right-aligned and its middle when centered. Centering also gives an odd spare cell to the leading side, as other layouts already do, so centered text with an odd number of spare cells sits one cell further right than in 0.0.4. ImageFit.fit centers an image with the same rounding.
  • LogicalKeyboardKey.forCharacter identifies a single Unicode scalar by the scalar itself, and keys with different labels never compare equal. Characters other than ASCII letters, digits, and space were identified by a hash that could collide, so a Shortcuts binding for one emoji could match another.
  • TextEditingController edits and cursor movement do nothing while its selection is cleared or lies outside the text, as on a new controller before a selection is set. insert, newline, deleteBack, and deleteForward return false and leave the text unchanged. insert and deleteForward used to throw a RangeError, and moveCursorRight skipped the start of the text. Set selection before editing programmatically.
  • Text, RichText, and RenderParagraph.selection reject a TextHighlight with a negative or reversed range by throwing an ArgumentError in release builds too. Only a debug assertion caught it before.
  • Select paints each grapheme cluster of an option's name and description whole. The earlier per-cell path kept only a cluster's first scalar, so a zero-width-joiner emoji lost its tail. DiffView pads split-view columns by terminal cells rather than UTF-16 code units, so wide text in the left column no longer moves the column divider.
  • A second Ctrl+C now stops dart run noir:run when the app ignores the first. The runner forwards the first SIGINT to the app, and an app that handles SIGINT and keeps running, such as one passing onExitRequested, used to leave the runner waiting on it; later presses were only forwarded again. The second now kills the app process.

Removed #

Breaking prerelease change: these unused members are removed without compatibility shims.

  • BoxShape and BoxDecoration.shape, with the shape constructor parameter. BoxShape.circle only suppressed the body fill; leave color unset instead.
  • Color.withOpacity. Pass the alpha channel to the constructor instead, as in Color(color.r, color.g, color.b, opacity).
  • Listenable.merge. Add the listener to each source instead.
  • TextRange.isNormalized, TextRange.textBefore, and TextRange.textAfter.
  • TextEditingValue.composing and the composing parameters of the TextEditingValue constructor and copyWith. Noir never set a composing range, and equality and toString no longer include one.
  • Actions.find and Actions.invoke. Actions.maybeFind returns null and Actions.maybeInvoke returns KeyEventResult.ignored where those threw.
  • Focus.of, Focus.maybeOf, FocusScope.of, and FocusScope.maybeOf. Pass a FocusNode to Focus or a FocusScopeNode to FocusScope and keep a reference to it instead.
  • OverlayPortalController.toggle. Call show or hide according to isShowing instead.
  • CursorController.updatePosition, from package:noir/noir_low_level.dart.
  • The undocumented TERMINAL_TUI_DEBUG_LOG=1 environment switch, which wrote a line to standard error for every rendered frame.

0.0.4 #

Adds opt-in caller-owned shutdown. The native ABI and bundled native artifacts are unchanged from 0.0.3.

Added #

  • runTuiApp(onExitRequested: ...) notifies the host once, synchronously, on the first termination signal or unhandled Ctrl+C with its conventional exit code. The host owns cancellation, awaiting outstanding work, terminal disposal, and final exit status; Noir does not await callback work or dispose the terminal. A synchronous callback failure propagates the original error after best-effort cleanup. Omitting the callback preserves default shutdown behavior. Drive mode does not install this signal hook.

0.0.3 #

This release fixes two failure paths and changes no public API. The native ABI and the bundled native artifacts are unchanged from 0.0.2.

Fixed #

  • A build that throws no longer strands the rebuilds queued behind it. BuildOwner.buildScope() restores every element the failed pass had not reached yet, in its original order, requests a frame for that work, and then rethrows the original error unchanged. The element that threw is not retried. A host that survives the error, such as a guarded zone or a test, paints the other elements' updates on the next frame instead of losing them for the rest of the session.
  • TuiApp.exit and TuiApp.requestExit report the exit code even when application cleanup throws. Disposal and the exit notification are both attempted, the requested code reaches the exit sink unchanged, and the first failure is rethrown afterwards. A drive-mode host whose cleanup fails now still shuts down instead of waiting on an exit that never arrives.

0.0.2 #

This release changes where Noir lives in its repository, not what it does. The package moved to packages/noir, next to noir_signals, so the README and the package metadata now link to the new paths. The library, its public API, and the bundled native artifacts are unchanged from 0.0.1.

Changed #

  • README, example, and notice links point at packages/noir in the repository. Links on the 0.0.1 pub.dev page that name the old root paths no longer resolve.
  • The package repository field names packages/noir.

0.0.1 #

This initial 0.0.x release adds LayoutBuilder and one framework lifecycle seam, fixes focus recovery and keyed ListView rows, and moves the opt-in hooks library to a separate package. The native ABI and the bundled native artifacts are unchanged from alpha.4.

Added #

  • Mouse pointer shapes for OSC 22 terminals: enabled buttons, checkboxes, switches, and sliders request a hand, text fields request an I-beam, and custom PointerListener regions can set mouseCursor. Pointer selection follows render-tree hit testing and updates after layout even when the mouse is stationary. High-level mouse reporting now includes movement by default; explicit enableMovement: false preserves click/drag-only reporting. iTerm2 uses legacy OSC 22 shape names and restores its default pointer on exit. Select and selectable ListView rows, including popup menus, show a hand only over valid visible options. TabSelect requests a hand over tab cells while leaving its underline and description unchanged. Unsupported terminals retain their own pointer.

  • Terminal output stays synchronous; removing redundant asynchronous stdout flushes prevents immediate mouse setup or cleanup from failing with a bound stream sink.

  • Added LayoutBuilder, which builds its child from the incoming BoxConstraints. The builder runs during layout and its result is laid out in the same frame, so the constraints it reads are the constraints its child receives. It runs on the first layout, on a constraint change, on a widget update, on an inherited dependency change, and on reassembly; repeated layout at unchanged constraints does not run it. A builder that throws leaves the element marked, so the next layout retries.

  • Added State.deferDispose and HookState.deferDispose. A host registers a cleanup for a resource it retires while the descendants built by the previous configuration may still read it. The framework releases that resource after the host successfully updates its descendants and the removed descendants finish unmounting, or during unmount before State.dispose runs. Batches release descendants before ancestors, registration order is preserved inside one host, and a failed initialization, widget update, or build keeps the resource alive until a later reconciliation succeeds.

Fixed #

  • A component rebuilding below Row, Column, Stack, or Wrap now reconnects a replacement render object in its existing child slot. Local changes to a component's root widget previously removed its visible output until the parent rebuilt. Sibling state and order, flex/position data, and pointer routing remain attached to the correct child.
  • Flexible, Expanded, and Positioned now preserve their layout behavior through non-render component wrappers, including metadata-only local updates. Invalid placement, duplicate metadata on one render edge, and non-positive widget flex values now reject in release mode instead of silently losing their layout behavior.
  • Plain ListView instances now preserve valid caller scroll positions when mounting, changing controllers, or changing item counts. Switching off selection keeps the current window, and simultaneous controller/selection changes follow the new highlight without first jumping to the old one.
  • The packaged hot-reload runner now recompiles detected edits whose file timestamps are not newer than the last successful reload, including recovery after a rejected reload. The VM's own timestamp filter skipped such an edit while reporting success, so the app reassembled stale code. Only that reload is forced, which recompiles every source and takes longer; an ordinary save is newer than the last reload and still takes the incremental path. The runner log names the path taken.
  • FocusManager now recovers focus after an involuntary loss. Disabling the focused control or removing the region that owns focus previously left the tree with no primary focus, and Shortcuts.handleKeyEvent routes from the focused element, so the tree stopped answering every binding. Recovery runs after the synchronous tree updates finish and focuses the first control inside the nearest surviving explicit scope, so bindings mounted inside that scope answer again; the scope itself takes focus only when it holds no control. It then falls back to the first node in traversal order, and leaves focus empty when nothing is eligible. An explicit requestFocus() and an incoming autofocus both claim focus first, and an intentional unfocus() is not recovered.
  • autofocus is now honored when it is enabled after mount.
  • A layout request raised during layout is now answered in the same frame. PipelineOwner.flushLayout used to run one pass and then discard any render object marked while that pass ran, for example by a LayoutBuilder whose build touched a render object already laid out or still performing layout, and nothing retried it. The flush now runs another full pass while such requests remain, up to three passes, and then throws a StateError naming the render objects that kept requesting layout from inside layout. A request followed by that object's layout in the same pass does not cost a second pass. Adopting a child during layout also invalidates its active parent and conservatively retries that parent. Architecture tests now freeze the layout-time build seam to LayoutBuilder and keep the rendering layer free of element-layer imports.
  • A keyed ListView row now names exactly one element. The row's LocalKey was copied onto the SizedBox wrapping it, so the key named two elements and a strict driver locator reported an ambiguous match. A keyed row that stays in the window still keeps its State while the window scrolls or the rows reorder, and a row that leaves the window is still disposed.
  • ListView now follows its highlight when its own height or item extent changes, so a resized list keeps the selected row on screen while the controller keeps a valid scroll position.
  • App startup now releases acquired resources if terminal-mode activation or final registration fails, preserving the original error if cleanup also fails. Rendererless headless mouse requests are rejected before mounting.
  • Changed terminal pixel measurements now repaint an idle scene and update natural image sizes without an unrelated widget rebuild. Duplicate and invalid measurements still leave an otherwise clean scene unpainted.
  • Windows terminal sessions now check for size changes every 100ms and update layout and rendering when dimensions change. Checks stop when the session closes and do not run in headless or non-terminal sessions.

Changed #

  • The task-list walkthrough is now a five-lesson tutorial. Lesson 1 stays in doc/getting-started.md; lessons 2 to 5 live in doc/tutorials/task-list/. Each lesson keeps its exact changes, complete runnable checkpoint, and screenshot, and its earlier checkpoints ship as runnable files under example/tutorials/task_list/. The shipped example/task_list.dart is the final checkpoint, so the tutorial and the example cannot drift apart. The old step anchors still resolve from the tutorial's first page.
  • doc/hooks.md and doc/signals.md are reference documents. Their introductions no longer restate the tutorial, and dependency setup points at the package overview.
  • The core example/ directory now carries the two first-app tutorial checkpoints under example/tutorials/first_app/. The published documentation captures its terminal frames from those exact files.

Removed #

  • Removed package:noir/hooks.dart. The hook runtime, the built-in resource hooks, and their guide now live in the optional companion package noir_signals, published from packages/noir_signals/ in this repository. Replace package:noir/hooks.dart with package:noir_signals/noir_signals.dart and add noir_signals to the application's dependencies. Rename HookWidget to SignalWidget, HookBuilder to SignalBuilder, and HookWidgetBuilder to SignalWidgetBuilder. Every hook function, return type, and lifecycle rule is unchanged: useState still returns a ValueNotifier and useTextEditingController still returns a TextEditingController.

0.0.1-alpha.4 #

This release corrects the package archive and moves checkout-only code to its owning directories. Noir's public API shapes and runtime behavior, native ABI, and bundled native artifacts are unchanged from alpha.3.

Changed #

  • Large example implementations now live under example/src/, while every runnable entry point remains directly under example/. Shared example chrome has one owner under example/src/shared/.
  • Package and contributor guidance now distinguish supported library surfaces, publishable examples, repository-only scripts, and website sources.
  • Checkout-only entry points bin/patch_manager.dart and bin/snapshot_scenes.dart now live under scripts/, and the Patch Manager implementation now lives under scripts/patch_manager/. Both entry points were already excluded from the published alpha.3 archive.

Fixed #

  • The archive now includes third_party/opentui-v0.5.1/LICENSE-YOGA and names Yoga v3.2.1 in THIRD_PARTY_NOTICES.md. The published 0.0.1-alpha.3 archive omitted this MIT license notice even though Yoga is compiled into the bundled OpenTUI libraries.

0.0.1-alpha.3 #

This release includes the work from the unpublished alpha.2 candidate; no 0.0.1-alpha.2 package was published.

Post-release notice: The published 0.0.1-alpha.3 archive omitted the Yoga v3.2.1 MIT license notice for the Yoga sources compiled into the bundled OpenTUI native libraries. The repository correction will ship in the next package version; the published alpha.3 archive cannot be changed.

Added #

  • Added Theme / ThemeData and the first themed component tier: ListView, Checkbox, Switch, Button, Divider, ProgressBar, Spinner, Badge, and DataTable. Existing inputs, selects, and scroll views also resolve omitted colors through the nearest theme.
  • Added the opt-in package:noir/hooks.dart library with HookWidget, state and effect primitives, async and listenable observation, and hooks for Noir controllers and animations.
  • Added whole-cell Stack / Positioned and Wrap; horizontal TabSelect; horizontal and vertical Slider; seven original AsciiFont treatments; and rich, selectable TextTable grids.
  • Added selectable CodeView, unified and split DiffView, and GitHub-flavoured MarkdownView with grapheme-safe selection, OSC52 copy, async highlighting, GFM tables, and semantic terminal hyperlinks.
  • Added TerminalImage and the stateful Image widget for PNG, JPEG, WebP, first-frame GIF, and raw RGBA sources. They negotiate Kitty, Sixel, or block rendering and support measured pixel sizing, fit modes, cancellation, and explicit ownership.
  • Added the root OverlayPortal / OverlayPortalController surface and launcher-anchored MenuAnchor / MenuController menus. This prerelease API does not claim Flutter's nested overlay, transform, animation, or cascade behavior.
  • Added Icons, a width-audited catalog of 119 single-cell glyph strings. Terminal icons remain plain text (Text(Icons.check)), not an Icon widget or IconData hierarchy.
  • Added the packaged dart run noir:run development command. It watches Dart sources, performs VM reload plus Noir reassembly, preserves the last good app after rejected edits, forwards arguments and exit status, and records diagnostics in .dart_tool/noir/run.log.
  • Added headless drive mode, with a repository-only Dart client and CLI, parser-backed key and pointer input, exact key/type/text locators, focus snapshots, and production hit-test-path clicks. scripts/noir_drive.dart accepts key space.
  • Added six guarded raw scissor- and opacity-stack operations to package:noir/noir_ffi.dart. These expose pinned OpenTUI availability only: its opacity stack does not fade ordinary text and cannot underpin an Opacity widget.
  • Added the live Pub search example with injected test data, stale-response suppression, paging, completion suggestions, anchored sort/filter menus, responsive package details, and rendered health and advisory documents.
  • Added opt-in chronological tail following to ScrollController through followTail and isFollowingTail. An opted-in controller starts at the trailing extent, follows content growth and reflow, detaches when scrolled above the end while preserving the top-row offset, and reattaches on return.
  • Added TextArea.maxHeight for bounded visual-row growth, softWrap for grapheme- and cell-aware wrapping, and submitOnEnter so Enter submits while a distinguishable Ctrl+J inserts a newline and multiline paste stays intact.
  • Added DiffView.canRequestFocus, so a read-only embedded preview stays out of the enclosing surface's keyboard traversal, and MarkdownView.tableCellPaddingX, which defaults to 0 to match the pinned OpenTUI Markdown renderer and leaves the choice to the application.
  • Added the agent chat example: a product-neutral transcript whose presentation consumes a semantic backend and never learns which agent produced an event. A deterministic offline replay backend is the default, and an opt-in bounded Claude CLI backend behind --claude=/absolute/path drives the same reducer and widget tree. The adapter starts claude -p without a PTY in safe mode, keeps tools, slash commands, MCP servers, and session persistence off, and exposes no permission channel.

Changed #

  • Simplified startup to runTuiApp(const MyApp(), enableMouse: true). runTuiApp owns hot-reload registration and terminal dimensions; TuiApp.exit(context) now performs in-tree disposal and exit-code handling.
  • Added BuildContext.mounted and State.reassemble(), hardened the State teardown window, made exact-type inherited lookups truly exact, and made framework-owned focus and text-controller replacement transactional.
  • Focus nodes can move between inactive elements in the same focus manager without losing focus. Focused lists, selects, and tables use the selected theme color and mute that highlight when focus moves elsewhere.
  • Standardized example chrome around terminal-native titled regions and moved Patch Manager's tracked-file presentation to DiffView without changing its staging and action model.
  • DiffView fills an added or removed row to the viewport edge, and a split row fills each half up to its own edge, which matches the pinned OpenTUI diff renderer. Earlier rows painted their background only under the text.
  • MarkdownView renders - for an unordered list item, which matches the pinned OpenTUI Markdown renderer. Earlier lists used a bullet glyph.

Fixed #

  • Restored legacy raw Ctrl+A selection and Ctrl+C copy in document views while preserving enhanced key reports with associated text. Interactive sessions request xterm modifyOtherKeys mode 2 so Ctrl+C remains application input through tmux, then restore xterm and Kitty keyboard modes independently.
  • Tightened renderer and terminal lifecycle cleanup: late resizes are ignored, closed sessions release renderer references, borrowed native buffer views are invalidated after render attempts, stale native draw stacks are cleared, and best-effort finalization cannot leak a cleanup exception.
  • Fixed flex overflow, decorated-child, and clipped-buffer painting at viewport boundaries; image replacement and cancellation ownership; one-ticker Spinner behavior; swapped ListView viewport anchoring; and Select scroll indicators under tighter layout constraints.
  • Pub search completion starts fresh hosted requests for each eligible prefix, keeps either successful endpoint when the other fails, and does not retain a stale completion corpus.
  • ImageProtocol.auto uses block cells under tmux. Forced Kitty graphics through tmux remain unsupported for alpha.3 because placement may stay displaced until a resize.
  • TextArea and TextInput paint each grapheme cluster whole. The earlier per-cell path kept only a cluster's first scalar, so an extended cluster such as a zero-width-joiner emoji lost its tail.
  • ScrollBox clips a descendant editor's terminal cursor to the same viewport as its painted cells, so an editor scrolled out of view no longer positions a stray cursor outside the viewport.

Removed #

  • Breaking prerelease change: removed the unused ColorSupport, TerminalCapabilities, TerminalSize, and CapabilitiesDetection helpers without compatibility shims.

0.0.1-alpha.1 #

  • The native-asset build hook now declares native_manifest.json and the selected bundled library as file-system dependencies. Dart can therefore invalidate cached hook output, repeat SHA-256 verification, and regenerate the derived macOS bundle copy when either input changes.
  • Added focused regression coverage for the hook dependency declarations and documented how to suppress routine build-hook status without skipping native asset verification.
  • Added the Like Reactor example, demonstrating deterministic heart particles, animation-driven morphing, and overlapping keyboard and mouse activation.

0.0.1-alpha.0 #

  • First public alpha of Noir's Flutter-like reactive widget framework for terminal applications.
  • Declarative stateless and stateful widgets, setState, layout, text styling, focus, keyboard and mouse input, text editing, scrolling, and animation.
  • Align positions naturally sized children within bounded boxes while still filling bounded axes and shrink-wrapping unbounded axes.
  • Hot reload support: TuiApp.reassemble() rebuilds every mounted element and forces a full layout and paint pass, preserving State, focus, scroll, and animation state and recreating no terminal or native resource.
  • Opt-in registerHotReloadExtension(app) publishes the ext.noir.reassemble VM service extension so a development driver can rebuild a running app after reloadSources.
  • The Flutter-inspired counter example composes a flat blue app bar, centered body, and solid square-style clickable action surface. Up/Down, +/-, Enter, Space, and left click drive its state; an unconsumed Ctrl+C key follows the terminal session's cleanup and interrupt exit path while higher-priority handlers can override it.
  • Terminal shutdown restores inherited line and echo modes before cancelling the stdin subscription, avoiding Dart/macOS EBADF errors during Escape or other normal disposal paths.
  • Terminal input preserves modifiers from xterm modifyOtherKeys reports, maps Home/Insert/End and function-key reports to pinned OpenTUI semantics, and retains press/repeat/release metadata from Kitty functional and tilde reports. The multiline examples use Ctrl+D as a portable submit key while TextArea still accepts Ctrl+Enter when the terminal reports that chord.
  • Bundled, SHA-256-verified OpenTUI native libraries for macOS, Linux, and Windows on x64 and arm64.
  • Canonical OpenTUI v0.5.1 source and unchanged official release assets.
  • macOS bundles require macOS 13.0 or later.
  • See Known Limitations for current platform and rendering constraints.
12
likes
150
points
548
downloads

Documentation

Documentation
API reference

Publisher

unverified uploader

Weekly Downloads

A Flutter-like reactive terminal UI framework for Dart, powered by OpenTUI.

Homepage
Repository (GitHub)
View/report issues
Contributing

Topics

#tui #terminal #cli #widget #ffi

License

BSD-3-Clause (license)

Dependencies

characters, code_assets, crypto, ffi, hooks, markdown, meta, vm_service

More

Packages that depend on noir