Counterpart of watermark-demo: the same transparent, undecorated,
taskbar-hidden overlay, stacked below everything instead of above. Holds the
Nucleus atom (copied from nucleus-demo) and a clock, drags itself through the
compositor since an undecorated window has no title bar to grab, and carries a
native context menu to lock its position or close it — no control window.
Linux takes an X11 surface of its own (forceX11), keep_below being an X11
protocol.
Animated NUCLEUS watermark in a transparent, undecorated, always-on-top,
click-through, never-focused window pinned to a screen corner, hidden
from the taskbar and controlled entirely from a ComposeNativeTray icon
(corner selection + quit).
The tray icon is fed as image files (iconPath/windowsIconPath as file:
URIs): the composable-icon overloads crash on the skiko shipped with
Compose 1.12 (NoSuchMethodError on Image.encodeToData), and java.net.URI
rejects raw Windows paths.
Worst-case shape for the RectManagerEdtGuard on the real Tao backend: a
throttled+debounced onLayoutRectChanged entry moving every frame, a
LazyColumn (mid-pass dispatchCallbacks), >16ms draw and placement
phases, and periodic remounts. The rect callback doubles as a detector:
if a RectManager delayed dispatch ever escapes to the EDT again, the
app prints the proof and exits 55; on an unguarded Compose 1.12 backend
the same shape crashes with 'LayoutNode not found in RectList'.
Also documents in RectManagerEdtGuard why the pin is complete on
1.12.0-rc01 even for debounced rect callbacks: triggerDebounced
early-returns on a pinned (future) deadline, and the mid-pass lowering
branch in ThrottledCallbacks.fireWithUpdatedRect/fire is an upstream
no-op that writes the old deadline back. onScenePulse stays as
defense-in-depth for the day upstream fixes that assignment.
See #555
Introduce a :spellcheck module (JNI Hunspell via dlopen) and SpellcheckContextMenu
for red-wave underlines plus Cut/Copy/Paste suggestions. Jewel uses ContextMenuDivider
via ProvideJewelSpellcheckMenu. macOS and Windows remain no-ops until implemented.
Add a headless smoke app that loads AppDatabase_Impl via
Class.forName(base + "_Impl") with no hand-written reachability entry.
Validates runGraalvmNative succeeds with the orphan detector and fails
with -Pnucleus.orphanDetect=false.
The Windows counterpart of the GStreamer sample: IMFSourceReader decoding
through DXVA2, the D3D11 video processor converting NV12 to RGBA, and the
frame published in a shared texture the compositor samples — nothing touches
the CPU.
The video processor is not a shortcut. A DXVA decoder hands out textures
carrying D3D11_BIND_DECODER and nothing else, so they cannot be bound as
shader resources; VideoProcessorBlt is the sanctioned way to read them and it
brings the colour matrix and nominal range along, which is why Chromium's
Windows video path uses it too. Output is R8G8B8A8_UNORM, checked with
CheckVideoProcessorFormat rather than degraded to BGRA, because the consumer
samples premultiplied RGBA and a swapped fallback would be silent. The alpha
fill mode is OPAQUE: a video has no alpha, and drivers are free to leave it
at zero, which composites the frame away.
The shared texture is sized from the first decoded frame, not from the
negotiated type: a decoder only settles its output type once it produces a
sample, and the size it reports then differs from the preroll one. Getting
this wrong is what makes the window come up with no picture. A media-type
change later on is tolerated as long as the frame size holds, since the
consumer has already imported a texture of it.
A source reader has no clock, so the helper paces itself: one sample is read
ahead and held until its presentation time is due against QPC. The Compose
loop ticks once per composited frame and is told "not yet", which plays the
file at its own rate whatever the display does. Software decode is covered as
well — the frames arrive in system memory and go through a staging upload
into the same video processor.
The file is picked with FileKit, or passed as an argument, or named in
NUCLEUS_MF_URL; a pick that cannot be opened leaves the previous one playing.
Verified on this machine (RTX 5060 Ti, ANGLE/D3D11): a 1680x1088 30 fps
recording plays at 30 frames/s with 9 recompositions of the video pane for the
whole run, and a 4K file plays with the same figures — the controls (clip,
alpha, rotation, crop, Compose overlay) apply to the frame, which is the point.
The helper is built by its own build.bat and stays out of CI; everything it
links against ships with Windows, and the sample says so when it is missing.
A sample that plays a real video file through TextureView: hardware decode,
GPU colour conversion, and the frame composited in the Compose scene without
ever touching the CPU.
The pipeline is uridecodebin ! glupload ! glcolorconvert ! appsink, asking for
RGBA in GL memory. Converting in the pipeline rather than in the compositor is
not a workaround: it is the same division of labour Flutter's Linux embedder
settled on, whose texture API takes a GL texture and treats it as RGBA. It also
sidesteps the interleaved-chroma formats Skia cannot sample.
Three EGL contexts, one share group: the window's, captured from inside a draw
pass because that is where it is current; one handed to GStreamer, which creates
its own sharing context from it — it has to activate what it is given, so it
must not be the window's, and its GL info has to be filled in first or the EGL
backend reaches for GLES and EGL refuses the share; and one for the puller, so a
producer loop on a background dispatcher never touches the render thread.
Each frame is copied into a texture of ours and handed over as the EGLImage that
aliases it, which keeps the source stable for the whole playback: one import, and
no recomposition per frame — the pane's recomposition counter is on screen next
to the frame rate to show it. The controls (clip, alpha, rotation, crop, Compose
overlay) are there to show the frame is a composable and not a hole punched
through the window.
Verified on this machine (NVIDIA 5060 Ti, Wayland): 1280x720 H.264 decoded with
nvh264dec, looping, ~1100 frames composited per run with no import failure, and
no crash on termination — the pipeline is stopped from a shutdown hook, because
its GL thread otherwise outlives the toolkit's context.
The helper is built by its own build.sh against the GStreamer development files
and stays out of CI, which has no GStreamer; the sample says so when it is
missing.
Adds an alternative to the fixed TitleBar layout so Compose content can
extend under the title bar, plus the primitives design systems need to
build their own chrome. All additive: existing DecoratedWindow/TitleBar
call sites are untouched.
- WindowScaffold: Docked (current behaviour) or Overlay placement, the
latter letting content fill the window while the bar floats above and
the measured bar height flows back as content padding.
- LocalWindowChromeInsets: reserved control zones (macOS traffic lights,
KDE edge padding) exposed to custom chrome.
- Modifier.windowDragArea: standalone drag region with double-click
maximize, extracted from TitleBar's implicit handler.
- Modifier.windowGlassRegion: Apple's wallpaper-tinted material behind a
Compose region, backed by a hosted NSSplitViewController so AppKit
applies the same desktop-tinted backdrop as System Settings.
- WindowBackdrop: full-window behind-window glass (NSGlassEffectView on
macOS 26, NSVisualEffectView fallback).
- WindowAppearance: forces NSWindow.appearance so native surfaces follow
the app theme rather than the OS setting.
Also honours Modifier.macOSLargeCornerRadius from WindowScaffold, and
routes nativeSetWindowBackgroundColor through the stored-background path
so the themed colour is applied according to the transparency mode.
GraalVM native-image testing (examples/tao-native-test):
- New runner app (nucleus plugin, graalvm enabled) that compiles the whole
Tao test pyramid into a native binary and RUNS it — the first place a
produced native image is actually executed in CI (test-graalvm.yaml only
compiled nucleus-demo before). Consumes decorated-window-tao's compiled
test classes via a taoTestArtifacts configuration.
- TaoSceneTestBattery: generated reflection-free registry of the stage-1
offscreen suite (60 cases) — JUnit discovery needs reflection metadata,
direct calls need none.
- Two process modes (battery / headful): the offscreen battery's global
Compose runtime state prevents the Tao loop from starting in the same
process, so CI compiles once and runs the binary once per mode.
- New tao-native-tests job in test-graalvm.yaml (3-OS matrix, Xvfb+openbox
on Linux). Local macOS: battery 60/60 and headful 6/6 with native=true.
Native accessibility protocol (scripts/ci/ + tao-a11y job in pre-merge.yaml):
- Verifies the OS-level a11y projection from the OUTSIDE, via the same
client APIs assistive tech uses, against tao-demo's A11y tab
(NUCLEUS_DEMO_TAB env added to tao-demo for direct navigation):
- Windows (hard gate): verify-uia.ps1 — UIA tree exposure, InvokePattern
round-trip (Increment -> 'click counter 1'), TogglePattern, RangeValue.
Derived from scripts/dump-uia-tree.ps1 / uia-action.ps1.
- Linux (hard gate): verify-atspi.py — pyatspi walk over the AT-SPI bus
(org.a11y.Status.IsEnabled flipped like scripts/a11y-linux-iter.sh),
element exposure + doAction round-trip.
- macOS: verify-ax.swift — AX client dump (axdump/axtool derived);
self-skips on runners without the TCC Accessibility permission.
Verified locally: 4/4 elements exposed, 53 named accessibles.
A plain Swing JFrame driven by TaoApplication.run (native Tao/winit event
loop on the main thread), with no Compose. Demonstrates AWT EDT + Tao loop
cohabiting in one process: buttons cross from the EDT into the Tao loop to
open/close a native Tao window. Forces exitProcess(0) after the loop stops
since AWT's non-daemon EDT would otherwise keep the JVM alive.
End-to-end host for the issue #317 sandbox pipeline: a Compose desktop app
that picks a file or folder (FileKit) and compresses it with zstd-kmp
(Zstandard). zstd-kmp loads its native library through the extract-and-load
pattern, so pairing it with TargetFormat.Pkg exercises the marker + bytecode
rewrite + runtime shim redirect in a signed store bundle.
- Pure-Kotlin streaming compressor (no upstream library changes).
- GraalVM native-image config: JNI reachability metadata for zstd-kmp's
native methods and result fields, plus -H:IncludeResources for the bundled
jni/<arch>/libzstd-kmp.* payloads.
- Drop the redundant local val + non-null assertion in the sandbox manifest
wiring.
Move all top-level demo/sample modules into a single examples/ directory with
uniform <topic>-demo naming (example → nucleus-demo, sample-tao → tao-demo,
jewel-sample → jewel-demo, etc.), and update every CI workflow, script, and doc
that referenced the old Gradle paths.
Add examples/compose-demo: a port of bitsycore/compose-desktop-native's demo
running on the Tao backend with Material 3 decorated windows, FileKit file
dialogs, Navigation 3, and GraalVM native-image config. Screens relying on
Material 3 Expressive (Compose 1.12) are omitted to stay on the repo's 1.11.x.
- Port TitleBarLayoutPolicy (fill-center) feature (#245) to the 2.0 API
- Thread layoutPolicy through BasicTitleBar in Tao backend
- Thread layoutPolicy through MaterialTitleBar (material3)
- Rewrite FillCenterDemoWindow on NucleusApplicationScope using nucleusWindow
- Drop unused window: Window param from TitleBarLayoutPolicy
New taskbar-progress-tao adapter module providing Tao-backed window support. Adds NucleusTaskbarProgress facade that transparently dispatches to correct backend (AWT or Tao) via runtime window type checking. All operations offloaded to daemon thread to prevent shutdown deadlocks. Refactored example to use backend-agnostic NucleusWindow API.
- New module: taskbar-progress-tao with TaoTaskbarProgress and NucleusTaskbarProgress
- Unified API: extension functions on NucleusWindow (transparent to callers)
- Threading: all native calls offloaded to executor (non-blocking, prevents freeze-on-close)
- Platform support: Windows ITaskbarList3 via HWND, macOS/Linux delegates to existing implementations
- Example refactored: removes backend-specific dispatch logic