Skip to content

JavaScript port: real DOM text, incremental semantics, and native-resolution rendering - #5552

Open
shai-almog wants to merge 126 commits into
masterfrom
js-port-web-native-overlay
Open

JavaScript port: real DOM text, incremental semantics, and native-resolution rendering#5552
shai-almog wants to merge 126 commits into
masterfrom
js-port-web-native-overlay

Conversation

@shai-almog

Copy link
Copy Markdown
Collaborator

What this does

Makes the JavaScript port feel like a web page without replacing the renderer.

The obvious approach -- emit DOM per component -- is the one Flutter and Uno both tried and reversed. What they kept is the useful half: canvas for pixels, a targeted DOM layer for text, semantics and input. Flutter's canvas apps ship <flt-semantics> for ARIA and <flt-text-editing-host> for a real positioned <input>. This PR takes the port to that shape.

The layers

Two DOM layers sit above the canvas, which is already role=presentation / aria-hidden:

  • #cn1-text-layer carries the visible text. BufferedGraphics.drawString hands each run to the layer instead of the canvas, so what the user sees is real text -- selectable, findable, rasterized by the browser. Codename One stays the sole layout authority: a run arrives already broken and placed, so it is one white-space:pre element at an absolute coordinate and the browser cannot wrap or reflow it. Text measurement is untouched and stays on the worker's OffscreenCanvas.
  • #cn1-accessibility-tree carries the ARIA projection, now updated incrementally.

Text that stays on the canvas by design: offscreen targets (transition buffers, paintLock, ComponentImage, screenshot -- a structural gate, since those use plain HTML5Graphics), cell renderers (one instance stamped at N positions cannot key a pooled element), anything outside the displayed form (a modal dialog paints the form beneath it as its backdrop), shape clips and non-identity transforms. Bitmap fonts need no exclusion -- Graphics.drawString renders a CustomFont itself and never reaches the implementation.

Bugs fixed along the way

The overlay was rebuilt on every invalidation. accessibilityTreeChanged did setInnerHTML("") and recreated every element with fresh listeners. CHANGE_BOUNDS is raised by every setX/setY/setWidth/setHeight, so this ran on every scroll step and discarded DOM focus and any in-progress text selection each time. Nothing selectable could live there until it diffed.

The port rendered at 1x on every HiDPI display. port.js pinned devicePixelRatio to 1 and called it working "in CSS pixels end-to-end". Codename One addresses device pixels -- the iOS port detects the retina factor and scales the values it hands the native primitives -- so this was a misreading of the API, and it meant the browser upscaled a 1x bitmap on every retina screen. Text showed it first. Now 750x1334 backing behind a 375x667 CSS box at dpr 2.

A password field could be edited in clear text. The constraint switch compared the whole value, but PASSWORD is a flag (0x10000), so PASSWORD | EMAILADDR matched no case and fell through to type="text".

Three worker/main-thread failures. Written when the port ran on the main thread, silently broken once it moved into a worker: history.pushState was a @JSBody and threw on every form change (the port logged that the back command would not work); every matchMedia query answered false, so dark mode was never detected on any browser, nor reduced motion or forced colors; isSetCursorSupported() reported false although every cursor is mapped and wired.

The rule worth remembering: a @JSBody runs in the worker. Anything touching window, document, history, matchMedia or navigator needs a host binding.

Editing

TextArea/TextField edit through a real native input positioned over the component -- the Android/iOS/JavaSE shape -- and that path is unchanged apart from the constraint fix and the metadata browsers actually read: inputmode for the on-screen keyboard, autocomplete for password managers and autofill, autocapitalize/spellcheck mapped from INITIAL_CAPS_*, SENSITIVE and NON_PREDICTIVE, and a field name from the component name. cn1$autocomplete overrides the token. The newer EditField/EditorView path (startTextInput, lightweight with native IME) is untouched.

Goldens

Canvas-only captures no longer contain the text, so the Playwright harness now takes the authoritative screenshot of the composited page and writes it over the app's file in the same directory. The shared cn1ss transport used by iOS/Android/watch/TV is not forked. Goldens are regenerated from this run's artifacts -- they change size (750x1334) and gain real text.

Verification

scripts/verify-javascript-web-overlay.mjs asserts the layers directly, which canvas rendering never allowed. Passing 10/10 against a real build at dpr 2, including a check that semantic elements keep their identity across invalidations -- it tags them with a JavaScript property and re-reads it, so a rebuild would fail it.

Known gaps, deliberately left

  • Drag-selection is off: the layer takes no pointer events so the canvas keeps hit testing. Find-in-page and assistive tech do not need hit testing; selection does, and enabling it needs the pointer-routing rework.
  • Vertical placement uses fontHeight() as the line box, which matches CN1's own layout metric but is approximate against browser font metrics to about a pixel.
  • Occlusion within a single form (a Sheet over text in the same form) is not handled; only cross-form occlusion is.

🤖 Generated with Claude Code

shai-almog and others added 7 commits August 15, 2026 22:17
accessibilityTreeChanged wiped the overlay with setInnerHTML("") and rebuilt
every element, with fresh event listeners, on each invalidation. Because
CHANGE_BOUNDS is raised by every setX/setY/setWidth/setHeight, that ran on every
scroll step and layout pass: a full re-marshal of the tree across the worker
bridge, and -- more importantly -- it discarded DOM focus and any in-progress
text selection each time. Nothing selectable or focusable could live in the
overlay while that was true.

Move the projection into JavaScriptSemanticOverlay, which keys elements by the
stable semantic node id and reuses them across invalidations. Attributes,
geometry, text and child ordering are diffed against retained worker-side state
so only real changes are written, and listeners are bound exactly once per
element. Nodes already in the correct slot are never re-inserted, which is what
keeps a focused or selected element from being moved.

The tree is now walked from getRootIds() through getChildIds() rather than over
the node map's iteration order, so sibling order is defined by the snapshot and
a child can never be visited before its parent exists.

LINK nodes become real <a> elements, for the status-bar URL preview,
middle-click and modifier-click to open in a new tab, and the native context
menu that an ARIA role alone cannot provide.

No DOM property is read back; structure and previously applied values are
tracked in the worker, so every bridge call stays a fire-and-forget write and
the port's no-barrier-reads invariant holds.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Canvas text is a bitmap. It cannot be selected or copied, the browser's
find-in-page cannot see it, and it rasterizes differently from the browser's own
text -- which is most of why the port does not read as a web page.

Intercept drawString on its way to the display surface and emit a positioned DOM
element instead. Codename One stays the sole layout authority: by the time a run
reaches the layer the line is already broken and placed, so each run is a single
white-space:pre element at an absolute coordinate and the browser cannot wrap or
reflow it. A metrics disagreement between measureText and DOM text layout can
therefore only show up as a sub-pixel rendering difference, never as a clipped
label. Measurement itself is untouched and stays on the worker's OffscreenCanvas.

The interception sits on BufferedGraphics, which is the display graphics.
Offscreen surfaces use plain HTML5Graphics, so text painted into a transition
buffer, a paint lock image, a ComponentImage or a screenshot still rasterizes
onto the bitmap those callers read back.

Each run is wrapped in an element clipped to the graphics clip in force when it
was drawn, which reproduces canvas behaviour for text in a scrolled container
without the layer knowing anything about the component hierarchy. Runs are
pooled per component and reused across repaints, since a scrolling list repaints
continuously and an element per run per frame would swamp the bridge.

Three cases deliberately stay on the canvas:

- Cell renderers. A renderer is one component instance stamped at N positions,
  so runs cannot be keyed by it -- every row would overwrite the previous row's
  element and only the last would survive.
- Anything outside the displayed form. The layer sits above the canvas as a
  whole, so nothing drawn on the canvas afterwards can occlude it; a modal
  dialog paints the form beneath it as its backdrop, and promoting that text
  would float it over the dialog.
- Shape clips and non-identity transforms, which have no faithful CSS
  equivalent here.

Bitmap fonts need no exclusion: Graphics.drawString renders a CustomFont itself
and never reaches the implementation.

The layer is aria-hidden so assistive technology keeps reading the semantic
overlay and no label is announced twice, and it takes no pointer events so the
canvas keeps ownership of hit testing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Three features were written when the port ran on the main thread and were never
adjusted for the worker, where the objects they reach for do not exist. Each one
failed silently.

History. pushHistoryState() was a @JSBody, so it was compiled into the worker
and threw on every form change -- the port logged "history.pushState not
supported. Back command will not work." on each navigation, and it was right.
Route the call through the window binding so it lands on the main thread, and
add a popstate listener that dispatches the current form's back command, so the
browser's Back button and the in-app back control finally agree. A popped entry
is replaced afterwards, or the second Back would leave the app while there was
still somewhere to go inside it.

Media queries. Dark mode, reduced motion, forced colors, increased contrast and
reduced transparency were all evaluated with window.matchMedia inside a @JSBody.
The worker has no matchMedia, so every one of them answered false: dark mode was
never detected on any browser. Evaluate them through a MediaQueryList binding on
the main thread, cache the result rather than re-reading it per call, and
refresh the cache from a change listener so switching the OS theme takes effect
without a reload.

Cursors. Every Codename One cursor is already mapped to its CSS equivalent and
wired to the hover path, but isSetCursorSupported() was never overridden, so the
documented capability check reported false to applications.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Canvas rendering could only ever be checked by comparing pixels. With text and
semantics in the DOM they can be asserted directly, which is both faster and
diagnostic -- a failure names what is wrong instead of reporting a pixel delta.

verify-javascript-web-overlay.mjs drives a built bundle and checks that text is
promoted to real DOM text, that the text layer is aria-hidden and takes no
pointer events, that the canvas is hidden from assistive technology, that
semantic nodes keep their identity across invalidations, and that history
entries are pushed on navigation. The identity check tags elements with a
JavaScript property and re-reads them after further invalidations: a rebuild
would drop the tag, which is exactly the regression that would silently take
focus and text selection with it.

STATUS.md now describes the two layers, the cases that deliberately stay on the
canvas, and the remaining gaps -- drag-selection, sub-pixel vertical placement,
and occlusion within a single form. It also records that the screenshot goldens
still need to move to page.screenshot() and be rebaselined, since a canvas
capture no longer contains the text.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Codename One addresses device pixels. The iOS port detects the retina factor and
multiplies/divides the values it hands the native primitives, so the framework
draws at the display's real resolution. This port pinned the ratio to 1 instead
and described that as working "in CSS pixels end-to-end", with ?pixelRatio=2 as
an opt-in.

That is a misreading of the API, and it costs image quality everywhere: with the
backing store equal to the CSS size, every HiDPI display renders a 1x bitmap that
the browser then upscales 2x or 3x. Text shows it first, because a stretched
glyph is obvious next to a browser-rendered one, but it softens everything.

Report window.devicePixelRatio instead. scaleCoord/unscaleCoord already convert
at the DOM boundary -- peers, overlays, pointer coordinates -- and nowhere else,
which is the same split the iOS port uses, so nothing above the boundary changes.
?pixelRatio=N still pins a factor for the screenshot harness and skin designer.
The duplicate default in the @JSBody fallback is updated too, so the two cannot
disagree.

The backing store is also rounded rather than truncated: at a fractional ratio
such as 1.5 a truncated store no longer equals cssSize * ratio, so the browser
rescales by a hair and softens every glyph edge again.

Verified in a browser at deviceScaleFactor 2: the canvas is now 750x1334 backing
behind a 375x667 CSS box, and the semantic overlay reports correctly converted
CSS-pixel bounds.

Also fixes a CSS ordering bug in the text layer: the font shorthand carries its
own line-height ("18.9px/1.0"), so emitting it after the explicit line-height
reset the line box the run was supposed to use.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
TextField and TextArea edit through a real native input positioned over the
component, the same shape the Android, iOS and JavaSE ports use. The element was
carrying almost none of the metadata a browser needs to treat it as a form
field, so autofill and password managers had nothing to work with and mobile
keyboards could not specialise.

The constraint switch was also wrong. A constraint is a base type in the low
bits with flags above it -- PASSWORD is 0x10000 -- but the code compared the
whole value, so a field declared PASSWORD | EMAILADDR matched no case and was
edited as clear text. Mask the base and test PASSWORD as the flag it is.

Beyond the type, the element now carries what browsers actually read:
inputmode selects the on-screen keyboard, autocomplete is what lets a password
manager or address autofill offer a value, and autocapitalize/spellcheck
reproduce what INITIAL_CAPS_*, SENSITIVE and NON_PREDICTIVE already mean on a
native platform. A component name becomes the field name so a manager can pair a
username with a password instead of seeing unrelated edits. Applications can
override the autocomplete token -- to separate a sign-in field from a
change-password field -- with the cn1$autocomplete client property.

The dormant TextAreaNativeOverlay path shares the same helper so the two cannot
drift.

Verified against a real field: autocomplete is now present where the element
previously had no autofill metadata at all.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The app captures its own screenshot by reading back the canvas and ships the
bytes over the shared cn1ss WebSocket transport. That readback is no longer the
whole picture: text is now promoted into a DOM layer above the canvas, so a
canvas-only capture is missing every label on screen.

The transport is shared with the iOS, Android, watch and TV jobs, so it should
not be forked for one platform. Take the authoritative capture in the
JavaScript-specific Playwright harness instead -- it screenshots the composited
page, canvas and overlays together -- and write it over the app's file in the
same directory. The delivered artifacts and the golden comparison are unchanged.

Captures are queued the moment the app reports its own, since the suite has not
advanced at that point and the page still shows the frame under test, and are
serialized and drained before teardown so the final test of a run does not lose
its screenshot and read as a golden miss. A capture failure leaves the app's
canvas-only PNG in place rather than dropping the test.

Verified locally: 28 composited 750x1334 captures where the canvas-only path
produced 375x667.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 9ffdd8b2e4

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/run-javascript-headless-browser.mjs Outdated
@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

✅ ByteCodeTranslator Quality Report

Test & Coverage

  • Tests: 525 total, 0 failed, 54 skipped

Benchmark Results

  • Execution Time: 18037 ms

  • Hotspots (Top 20 sampled methods):

    • 19.26% java.util.ArrayList.indexOf (316 samples)
    • 6.28% com.codename1.tools.translator.BytecodeMethod.addToConstantPool (103 samples)
    • 3.23% com.codename1.tools.translator.Parser.cn1EnsureSubclassIndex (53 samples)
    • 2.99% com.codename1.tools.translator.BytecodeMethod.optimize (49 samples)
    • 2.99% com.codename1.tools.translator.ByteCodeClass.hasDeclaredMethod (49 samples)
    • 2.80% java.lang.StringBuilder.append (46 samples)
    • 2.50% org.objectweb.asm.tree.analysis.Analyzer.findSubroutine (41 samples)
    • 2.13% org.objectweb.asm.tree.analysis.Analyzer.analyze (35 samples)
    • 1.95% java.lang.Object.hashCode (32 samples)
    • 1.89% java.lang.System.identityHashCode (31 samples)
    • 1.83% java.util.HashMap.hash (30 samples)
    • 1.71% com.codename1.tools.translator.Parser.generateClassAndMethodIndexHeader (28 samples)
    • 1.46% com.codename1.tools.translator.Parser.classIndex (24 samples)
    • 1.40% java.lang.String.equals (23 samples)
    • 1.22% com.codename1.tools.translator.BytecodeMethod.equals (20 samples)
    • 1.22% java.lang.StringCoding.encode (20 samples)
    • 1.10% java.util.TreeMap.getEntry (18 samples)
    • 1.10% org.objectweb.asm.ClassReader.readCode (18 samples)
    • 0.98% com.codename1.tools.translator.bytecodes.Invoke.resolveDirectTarget (16 samples)
    • 0.91% java.io.FileOutputStream.open0 (15 samples)
  • ⚠️ Coverage report not generated.

Static Analysis

  • ✅ SpotBugs: no findings (report was not generated by the build).
  • ⚠️ PMD report not generated.
  • ⚠️ Checkstyle report not generated.

Generated automatically by the PR CI workflow.

@github-actions

github-actions Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

✅ Continuous Quality Report

Test & Coverage

Static Analysis

  • SpotBugs [Report archive]
    • ByteCodeTranslator: 0 findings (no issues)
    • android: 0 findings (no issues)
    • codenameone-maven-plugin: 0 findings (no issues)
    • core-unittests: 0 findings (no issues)
    • ios: 0 findings (no issues)
  • PMD: 0 findings (no issues) [Report archive]
  • Checkstyle: 0 findings (no issues) [Report archive]

Generated automatically by the PR CI workflow.

@github-actions

Copy link
Copy Markdown
Contributor

Cloudflare Preview

Three defects from review, all real.

Capture raced the suite. The composited screenshot was taken when a console
marker appeared, but that marker is emitted before the WebSocket send and the
suite advances from its own ACK, so nothing held the app still: a screenshot
could land after the next test had drawn its form and be written under the
previous test's name. Drive the capture from a hook the page calls instead --
browser_bridge.js awaits __cn1CompositeCapture inside the screenshot host call,
and the worker is blocked on that call, so the suite genuinely cannot advance
while the screenshot is taken. The test name now comes from the suite's own
"starting test=" marker rather than being parsed out of the capture line.

Paint hooks fired for offscreen renders. beforeComponentPaint/afterComponentPaint
run for every paint, including Component.toImage(), ComponentImage, paint locks
and drag images. Those draw through a plain HTML5Graphics, so no run is
promoted -- which meant the frame opened and closed around them saw zero runs
and released the component's real on-screen DOM text. Creating a drag image
could blank the labels underneath it until the next repaint. Only open a frame
when the graphics is the display graphics.

Hidden components kept their runs. The synchronization pass only asked whether a
component was still on the displayed form. Hiding a component, or any ancestor
of it, stops it painting without detaching it, so its pooled runs were never
refreshed and never released -- they stayed above the canvas after the parent's
repaint had already cleared the pixels beneath them. Retention now also requires
the component to be displayable.

Verified locally with the bridge hot-swapped into a built bundle: the hook
installs, captures are page-driven, and test names track the suite.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: e157713e1f

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/run-javascript-headless-browser.mjs Outdated
Second round of review, three defects.

The composited PNG was being overwritten. The harness wrote it into the cn1ss
directory while the screenshot host call was still running; that call then
returned the canvas-only data URL, the app sent those bytes over the WebSocket,
and the server wrote them over the composited file. Hand the image back through
the hook instead and return it as the screenshot, so the bytes travel the normal
path and the cn1ss server stays the only writer of the PNG. There is no longer
any ordering to get wrong, and the harness no longer needs the output directory
or the test name at all.

The glass pane and a dragged component paint on the canvas after the children,
so they cannot cover promoted text -- the layer is above the canvas as a whole.
Suspend promotion while a form has either, which puts the text back on the
canvas where paint order still decides what is on top. This is the same
reasoning already applied to modal dialogs, extended to the in-form cases.

Back reached the root form and then did nothing. Popping an entry and going back
one form already keeps history depth and navigation depth in step, because every
form show pushes an entry; pushing a replacement after handling a pop left a
dead entry, so from the root form the first Back was swallowed and only the
second left the app. Drop the replacement push.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 94f5e7209e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread scripts/run-javascript-headless-browser.mjs Outdated
@shai-almog

shai-almog commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 149 screenshots: 149 matched.
Native Windows port, REAL shipping pipeline: the hellocodenameone screenshot suite rendered by a binary CROSS-COMPILED on Linux (clang-cl + xwin, WebView2 linked) and RUN on a Windows x64 runner. Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 63ms / native 4ms = 15.7x speedup
SIMD float-mul (64K x300) java 63ms / native 5ms = 12.6x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 201.000 ms
Base64 CN1 decode 140.000 ms
Base64 SIMD encode 104.000 ms
Base64 encode ratio (SIMD/CN1) 0.517x (48.3% faster)
Base64 SIMD decode 95.000 ms
Base64 decode ratio (SIMD/CN1) 0.679x (32.1% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 28.000 ms
Image createMask (SIMD on) 23.000 ms
Image createMask ratio (SIMD on/off) 0.821x (17.9% faster)
Image applyMask (SIMD off) 70.000 ms
Image applyMask (SIMD on) 61.000 ms
Image applyMask ratio (SIMD on/off) 0.871x (12.9% faster)
Image modifyAlpha (SIMD off) 62.000 ms
Image modifyAlpha (SIMD on) 57.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.919x (8.1% faster)
Image modifyAlpha removeColor (SIMD off) 76.000 ms
Image modifyAlpha removeColor (SIMD on) 64.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.842x (15.8% faster)

@shai-almog

shai-almog commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 149 screenshots: 149 matched.
Native Windows port (x64 / Intel-AMD): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, SSE2 SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 48ms / native 3ms = 16.0x speedup
SIMD float-mul (64K x300) java 47ms / native 4ms = 11.7x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 158.000 ms
Base64 CN1 decode 104.000 ms
Base64 SIMD encode 74.000 ms
Base64 encode ratio (SIMD/CN1) 0.468x (53.2% faster)
Base64 SIMD decode 74.000 ms
Base64 decode ratio (SIMD/CN1) 0.712x (28.8% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 24.000 ms
Image createMask (SIMD on) 22.000 ms
Image createMask ratio (SIMD on/off) 0.917x (8.3% faster)
Image applyMask (SIMD off) 50.000 ms
Image applyMask (SIMD on) 37.000 ms
Image applyMask ratio (SIMD on/off) 0.740x (26.0% faster)
Image modifyAlpha (SIMD off) 208.000 ms
Image modifyAlpha (SIMD on) 33.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.159x (84.1% faster)
Image modifyAlpha removeColor (SIMD off) 43.000 ms
Image modifyAlpha removeColor (SIMD on) 36.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.837x (16.3% faster)

@shai-almog

shai-almog commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 149 screenshots: 149 matched.
Native Linux port (x64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub x64 runner. Baseline: scripts/linux/screenshots.

@shai-almog

shai-almog commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 149 screenshots: 149 matched.
Native Linux port (arm64), GTK3/Cairo/Pango, ParparVM bytecode-to-C (no JVM): the hellocodenameone screenshot suite rendered by a native ELF built + run on the GitHub arm64 runner. Baseline: scripts/linux/screenshots-arm.

@shai-almog

shai-almog commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 149 screenshots: 149 matched.
Native Windows port (arm64 / Apple Silicon - Arm): full hellocodenameone screenshot suite rendered offscreen with Direct2D/DirectWrite, plus the real benchmarks (base64 native/CN1/SIMD, image createMask/applyMask/modifyAlpha/PNG/JPEG, NEON SIMD kernels). Compared against the in-repo baseline in scripts/windows/screenshots.

Benchmark Results

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 57ms / native 3ms = 19.0x speedup
SIMD float-mul (64K x300) java 55ms / native 3ms = 18.3x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 native bridge unavailable (CN1 + SIMD + image benchmarks only)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path gated to scalar (CPU autovectorizes scalar; explicit SIMD not beneficial here)
Base64 CN1 encode 246.000 ms
Base64 CN1 decode 128.000 ms
Base64 SIMD encode 64.000 ms
Base64 encode ratio (SIMD/CN1) 0.260x (74.0% faster)
Base64 SIMD decode 63.000 ms
Base64 decode ratio (SIMD/CN1) 0.492x (50.8% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 12.000 ms
Image createMask (SIMD on) 8.000 ms
Image createMask ratio (SIMD on/off) 0.667x (33.3% faster)
Image applyMask (SIMD off) 24.000 ms
Image applyMask (SIMD on) 19.000 ms
Image applyMask ratio (SIMD on/off) 0.792x (20.8% faster)
Image modifyAlpha (SIMD off) 17.000 ms
Image modifyAlpha (SIMD on) 12.000 ms
Image modifyAlpha ratio (SIMD on/off) 0.706x (29.4% faster)
Image modifyAlpha removeColor (SIMD off) 21.000 ms
Image modifyAlpha removeColor (SIMD on) 13.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.619x (38.1% faster)

… DOM text

Regenerated from the CI run's delivered artifacts rather than locally, so the
fonts and rasterization match the environment the comparison runs in.

Two things changed at once. The port now renders at the display's real pixel
ratio, so captures are 750x1334 device pixels instead of a 375x667 1x surface
the browser used to upscale. And text is promoted into a DOM layer, so the
authoritative capture is a screenshot of the composited page rather than a
canvas readback.

Images the app renders offscreen and delivers itself -- the per-appearance theme
captures, for instance -- are unaffected by the second change and still carry
their text, because an offscreen paint goes through plain HTML5Graphics and
never promotes.

180 of the 181 goldens are replaced. ToastBarTopPosition is not delivered by the
suite (a parked test) so its golden is left alone rather than being deleted.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7fcccf5934

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@shai-almog

shai-almog commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 143 screenshots: 143 matched.
✅ Native iOS screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 1258 seconds

Build and Run Timing

Metric Duration
Simulator Boot 111000 ms
Simulator Boot (Run) 1000 ms
App Install 15000 ms
App Launch 4000 ms
Test Execution 482000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 94ms / native 3ms = 31.3x speedup
SIMD float-mul (64K x300) java 63ms / native 3ms = 21.0x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 227.000 ms
Base64 CN1 decode 152.000 ms
Base64 native encode 1041.000 ms
Base64 encode ratio (CN1/native) 0.218x (78.2% faster)
Base64 native decode 968.000 ms
Base64 decode ratio (CN1/native) 0.157x (84.3% faster)
Base64 SIMD encode 114.000 ms
Base64 encode ratio (SIMD/CN1) 0.502x (49.8% faster)
Base64 SIMD decode 59.000 ms
Base64 decode ratio (SIMD/CN1) 0.388x (61.2% faster)
Base64 encode ratio (SIMD/native) 0.110x (89.0% faster)
Base64 decode ratio (SIMD/native) 0.061x (93.9% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 11.000 ms
Image createMask (SIMD on) 3.000 ms
Image createMask ratio (SIMD on/off) 0.273x (72.7% faster)
Image applyMask (SIMD off) 368.000 ms
Image applyMask (SIMD on) 252.000 ms
Image applyMask ratio (SIMD on/off) 0.685x (31.5% faster)
Image modifyAlpha (SIMD off) 189.000 ms
Image modifyAlpha (SIMD on) 526.000 ms
Image modifyAlpha ratio (SIMD on/off) 2.783x (178.3% slower)
Image modifyAlpha removeColor (SIMD off) 275.000 ms
Image modifyAlpha removeColor (SIMD on) 108.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.393x (60.7% faster)

@shai-almog

shai-almog commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 144 screenshots: 144 matched.
✅ Native Apple TV (tvOS, Metal) screenshot tests passed.

The screenshot rebaseline landed 180 of 181 goldens byte-identical, but
ToastBarTopPosition stopped delivering: "timeout waiting for DONE
stage=capture-requested". The suite blocks on the capture host call, which now
awaits the composited screenshot, and `animations: 'disabled'` makes Playwright
wait for animations to settle -- a screen with a running toast never settles, so
the capture outlived the suite's own timeout. Drop that option and bound the
screenshot with an explicit timeout and a race, so this call always resolves
promptly. Falling back to the canvas readback costs the DOM text in one golden;
hanging costs the whole test.

Suspension was decided too late. It was applied while flushing, by which point
the components had already painted under the old setting -- their strings either
promoted and left off the canvas, or rasterized onto it -- so flipping the flag
afterwards left the frame either missing its text or showing it twice. Decide it
at the start of the frame instead, before any component paints.

Paint locking is off for this port. A locked component serves a cached image and
returns from paintInternal() before the per-component hooks run, so it stops
reporting text while its DOM runs stay on screen: the image's rasterized text
and the live DOM text are then both visible. The two representations cannot be
reconciled and the lock is only an optimisation, so it is disabled.

The semantic overlay wiped its own children. setTextContent replaces every
child, so relabelling a STATIC_TEXT or HEADING node detached its semantic
children and custom-action buttons while the retained ordering still claimed
they were attached -- the reconcile pass then skipped re-adding them and those
controls disappeared until an unrelated structural change rebuilt them. The
label now lives in a child of its own.

The root form pushed a dead history entry. It has nothing behind it, so the
first Back popped that entry without navigating and leaving the app from the
root took two presses.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f210351d0e

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@shai-almog

shai-almog commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 149 screenshots: 149 matched.
✅ Native iOS Metal screenshot tests passed.

Benchmark Results

  • VM Translation Time: 0 seconds
  • Compilation Time: 1033 seconds

Build and Run Timing

Metric Duration
Simulator Boot 81000 ms
Simulator Boot (Run) 1000 ms
App Install 12000 ms
App Launch 3000 ms
Test Execution 385000 ms

Detailed Performance Metrics

Metric Duration
SIMD kernel backend SSE2 (x64) / NEON (arm64) native kernels
SIMD int-add (64K x300) java 65ms / native 3ms = 21.6x speedup
SIMD float-mul (64K x300) java 85ms / native 3ms = 28.3x speedup
SIMD kernel correctness PASS (native result == scalar reference)
Base64 payload size 8192 bytes
Base64 benchmark iterations 6000
Base64 SIMD byte path active (NEON-accelerated)
Base64 CN1 encode 162.000 ms
Base64 CN1 decode 96.000 ms
Base64 native encode 531.000 ms
Base64 encode ratio (CN1/native) 0.305x (69.5% faster)
Base64 native decode 284.000 ms
Base64 decode ratio (CN1/native) 0.338x (66.2% faster)
Base64 SIMD encode 49.000 ms
Base64 encode ratio (SIMD/CN1) 0.302x (69.8% faster)
Base64 SIMD decode 45.000 ms
Base64 decode ratio (SIMD/CN1) 0.469x (53.1% faster)
Base64 encode ratio (SIMD/native) 0.092x (90.8% faster)
Base64 decode ratio (SIMD/native) 0.158x (84.2% faster)
Image encode benchmark iterations 100
Image createMask (SIMD off) 8.000 ms
Image createMask (SIMD on) 2.000 ms
Image createMask ratio (SIMD on/off) 0.250x (75.0% faster)
Image applyMask (SIMD off) 55.000 ms
Image applyMask (SIMD on) 176.000 ms
Image applyMask ratio (SIMD on/off) 3.200x (220.0% slower)
Image modifyAlpha (SIMD off) 216.000 ms
Image modifyAlpha (SIMD on) 259.000 ms
Image modifyAlpha ratio (SIMD on/off) 1.199x (19.9% slower)
Image modifyAlpha removeColor (SIMD off) 299.000 ms
Image modifyAlpha removeColor (SIMD on) 181.000 ms
Image modifyAlpha removeColor ratio (SIMD on/off) 0.605x (39.5% faster)

Dropping `animations: 'disabled'` from the capture was wrong. Playwright uses it
to settle animations and wait for fonts, and without it the screenshot races the
canvas presentation: 59 of 181 goldens went from equal to different, the static
graphics and chart tests among them. Restore it. The timeout and race stay, so a
screen that never settles still cannot stall the suite -- it falls back to the
canvas readback, which costs the promoted text in that one golden rather than
the whole test.

SENSITIVE now beats PASSWORD when choosing autocomplete. A field marked both was
getting "current-password", which is an explicit invitation to offer a stored
value, while SENSITIVE asks that it never be retained for completing schemes.

Custom accessibility actions refresh their label. Replacing an action with the
same id and new wording -- an Expand that becomes a Collapse -- left the
retained button announcing the old text until the action was removed entirely.

Buffered transitions suspend the text layer again. A fade renders only its
prebuilt images and never puts a component through the display graphics, so
moving the suspension decision to the start of a component paint meant it never
ran during one: the outgoing form's DOM text stayed fixed above the animation
for its whole duration. The drain checks it too, and only ever suspends there --
resuming stays at the start of a frame, before any component paints.

Two history corrections. An in-app back command reaches setCurrentForm looking
exactly like forward navigation, so it pushed an entry instead of spending one;
returning to the form we came from is now recognised as backward. And a pop
guard that refuses a back command leaves the same form showing after the entry
has already been consumed, so the entry is put back rather than letting the next
Back leave the app without asking the guard again. Where no back command exists
at all, Back now keeps unwinding instead of being swallowed.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 175a9b204b

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@shai-almog

shai-almog commented Aug 16, 2026

Copy link
Copy Markdown
Collaborator Author

Compared 217 screenshots: 217 matched.
✅ Native Apple Watch (watchOS, Core Graphics) screenshot tests passed.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 898aadc2a2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/BufferedGraphics.java Outdated
Both tests were asked about the whole run, so a clip whose bounds fall short of
the glyphs still had the draw's outline consulted about text it could not touch.
Each is now asked about the part of the run the clip's bounds reach, and a clip
that reaches none of it answers on its own.

What remains is that both can answer yes about one rectangle while touching
different parts of it. The exact answer is a polygon intersection, and the code
comment records why it is not attempted: getting it wrong the other way leaves
glyphs above a draw that covered them, which is wrong on screen, while answering
too generously only sends a component's text back to the canvas.

The 52 screenshots the clip and transform tests deliver are pixel-for-pixel
identical to the run before this change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 38ad81c878

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

shai-almog and others added 2 commits August 18, 2026 19:33
The actions region is created the first time a node needs a control, which
happens while the tree is still being walked -- before the roots are attached --
so it stayed ahead of the whole form in document order. A keyboard or
screen-reader user met "Set text" and "Delete" before reaching the field or item
that gives them their meaning.

It is moved behind the roots each time the tree is reconciled. Verified on the
TextFieldTheme screen: the region is the last child of the overlay, with its
three controls after the form.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Captures asked for while one was waiting for the rasterizing repaint were drained
after the leading callback ran, so a callback that threw took the rest of the
list with it: the flag that sends a capture to the queue was already down, so
every later capture took the immediate path and walked past the ones still
standing there.

The list is taken and emptied before any of them run, and each runs on its own.
These callbacks belong to unrelated callers that happened to ask during the same
frame, so one failing is no reason for the others to go unanswered.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 30fafdeac2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/BufferedGraphics.java Outdated
A clip stays fixed in the coordinates it was installed in while a draw is in the
coordinates in force now, and the two were intersected as though they shared a
space. Clip screen x=0..50, translate by 100, then fill x=-100..50: the canvas
paints screen x=0..50 and the report landed on screen x=100..150, so text the
fill really did cover was left above it -- the mistake that shows on screen
rather than the one that only sends text back to the canvas.

Both are now projected through their own transform and intersected on screen,
which is where the canvas resolves them. The clip and transform screenshots are
pixel-for-pixel identical across the change: 53 tests, no differences.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 437089f5a9

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/BufferedGraphics.java Outdated
shai-almog and others added 2 commits August 18, 2026 21:02
The coverage test walked every outline as a closed loop, so a partial drawArc or
an open subpath of drawShape was given a chord across its ends that the canvas
never strokes. Text beside the curve was detached for crossing a line that was
not there, and a detached component stays on the canvas for the rest of the form.

An outline now says whether it closes by repeating its first point -- a rectangle,
a polygon, a rounded rectangle, a wedge, a whole ellipse and a subpath that ends
in SEG_CLOSE all do; a partial arc and an open subpath do not. The segment walk
follows the points it is given, and only a fill adds the closing edge, because
context.fill() really does paint it.

Checked both ways on a 90-degree arc: a rectangle on the chord between its ends
is no longer reported, one on the curve still is, and a stroked rectangle keeps
its closing edge. The arc and clip screenshots are unchanged: 64 tests, no pixel
differences.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Every keystroke in a SET_TEXT control has already reached the framework, so when
focus leaves, an obscured field's control has nothing left to hold -- and what it
held was a secret. type="password" only stops it being read off the screen; the
value stayed in the DOM for a script or an inspector, and the sync pass would not
clear it, because it leaves a masked field alone precisely so it never writes the
secret back.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7ddd6b0ec2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

shai-almog and others added 2 commits August 18, 2026 22:04
The port claimed the entry its first form appeared on by replacing that entry's
state. The state there is not the port's to take: a page that embeds the canvas
keeps its own router data on it, and a host that finds its state replaced loses
the navigation it was in the middle of.

Nothing is written now. The entry is recognised instead: an id above everything
this session has pushed cannot be one of ours, because a traversal can only reach
entries the port created, so it is the entry the page was loaded on carrying an
id a previous life of the application left there. It is read as that rather than
as a step forward, which is what the rewrite was for.

verify-javascript-web-overlay.mjs now counts calls to history.replaceState from
the moment the page loads: 1 with the build that rewrote the entry, 0 with this
one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A capture that turns the text layer off has to wait for the frame that puts the
text back on the canvas, and that wait counted event-thread hops: two, on the
reasoning that the first lands before the repaint and the second after it. A
repaint is not one flush. This application's image grid paints over several, so
the read could land between them and come back with the last panels blank --
graphics-draw-image-rect failed exactly that way twice in CI, both times with
byte-identical output, while the same test renders identically on either side of
the change locally.

The read now waits for the flushes to stop rather than for a fixed number of
hops, bounded so a form that never stops flushing still gets its answer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f3942f28e7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

The control carried no maxlength, so it was the long way round an application's
limit -- and not only for one edit: TextArea.setText() raises maxSize to fit
whatever it is given, so text typed past the limit through this control moved the
limit for good.

The control now takes its length from the field it writes to, and a field with no
limit has the attribute removed rather than keeping one it no longer asks for.

Verified on the TextFieldTheme screen: both inputs and the textarea come up with
maxlength=124, the fields' own maximum.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 5360177503

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

The limit was applied on the way past two early returns, so a masked field's
control -- and one being typed into -- kept whatever limit it was built with. An
application that lowers setMaxSize() while a password field is on screen would
have left that control accepting the old length, and a value typed past the new
limit raises maxSize again on the way in, which is the same bypass one step
removed.

It is applied first now, before anything that returns.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: bff53963a3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/BufferedGraphics.java Outdated
Comment thread Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/BufferedGraphics.java Outdated
… the alpha

Two of the same faults the arc had, in the executable that fillRadialGradient
uses. It swept the long way round, so fillRadialGradient(.., 20, 200) painted the
other 160 degrees -- the wedge came out mirrored against the Android golden, and
text in the part it really painted kept the DOM. And its setGlobalAlpha call is
commented out, so it paints whatever the graphics alpha is, while coverage was
skipping the report when that alpha was zero: text left above pixels the fill had
in fact replaced.

The sweep now follows the sign of the arcAngle, as FillArc does. Coverage reports
this fill without consulting the alpha, because the renderer does not consult it
either -- what the canvas does is what coverage has to describe.

graphics-draw-gradient changes with the sweep and is rebaselined from CI next.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: da70c059d7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/BufferedGraphics.java Outdated
Comment thread Ports/JavaScriptPort/src/main/java/com/codename1/impl/html5/BufferedGraphics.java Outdated
shai-almog and others added 2 commits August 19, 2026 04:22
Two more places where coverage and the canvas disagreed. DrawCanvas is built with
alpha 255 and blits the GPU surface opaquely, but the report went through the
guard that drops a fully transparent draw, so a composite made under a
transparent graphics left text above pixels it had replaced. And a fillArc or
drawArc of zero degrees paints nothing at all, while the coverage outline was the
line from the centre out to the rim -- text across that line was taken off the
layer, for good, by a draw that touched no pixel.

The blit is counted whatever the alpha is; a zero sweep is not counted at all.
The 65 screenshots the arc and clip tests deliver are unchanged apart from the
gradient this branch already rebaselines.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The golden recorded the complement the partial radial gradient used to paint.
Taken from the CI run for da70c05, where it was the only test that differed:
pass=168, fail=1, and the wedge now matches the Android golden.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 691c44f339

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread Ports/JavaScriptPort/src/main/java/com/codename1/html5/js/dom/PopStateEvent.java Outdated
History state is any structured-cloneable value, so a page embedding this
canvas is free to keep its own router object on an entry. Binding getState
as String put that value through a conversion it cannot satisfy, when all
the port ever needs is to recognise that the value is not one of its own
stamps. Widen the binding to Object and let parseHistoryIndex inspect it as
a string only when it actually is one -- anything else reads as foreign,
which is exactly how a host page's entry should read.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 54459a3883

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

shai-almog and others added 2 commits August 19, 2026 06:07
Editing through the accessibility overlay's SET_TEXT control is still
editing that field, but the control was built as a plain type="text" with
no inputmode, autocomplete, autocapitalize, spellcheck or name. A screen
reader user editing an email, numeric, phone or URL field got the wrong
keyboard, and -- worse -- a field marked SENSITIVE or NON_PREDICTIVE had
prediction and autofill enabled on the one surface that could type into
it. The editor's applyInputConstraints already resolves all of this, so
it is shared rather than restated: a constraint added to one now reaches
both. Whether to write a type is passed in, because the control's shape
is this class's decision -- an obscured field gets a masking single-line
input whether or not the component is multiline -- and masking stays with
the snapshot's obscured flag, which an application can set either way.

Refreshed on every sync, so a constraint changed while the field is on
screen is not left behind.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
A field that exposes SET_TEXT was reachable twice over: its semantic
element carried tabindex=0 from isFocusable(), and the control that
actually edits it is separately focusable. The first stop is the useless
one -- the input handlers live on the control, so a keyboard or screen
reader user landed on a textbox that announced the field and then refused
every keystroke, before reaching the one that works. The semantic owner
now yields its stop to the control that represents it, decided from the
snapshot rather than from the entry's controls because the controls are
built after the attributes are applied.

The verify script asserts no field is tabbable alongside its control.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 454fafee82

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Now that the field itself is no longer a tab stop, its SET_TEXT control is
the only way a keyboard or screen-reader user reaches it -- and the control
recorded the editing marker without ever dispatching the node's FOCUS
action, which the semantic element it replaced did dispatch. The browser
therefore edited the field while Codename One still considered the
previously focused component active, so focus styling, focus listeners and
any keyboard navigation from there acted on the wrong component. FOCUS is
added to every focusable node by AccessibilityManager and its handler calls
requestFocus, so dispatching it is what moves the framework's own focus.

Recorded before the dispatch, matching the semantic element's listener: the
snapshot that comes back reporting the node focused then finds the overlay
already agreeing and does not pull DOM focus onto the semantic element,
which would take it off the control mid-edit. Focus in the other direction
follows the control too -- applyFocus targets it rather than the element,
and losing framework focus blurs it -- because the element it would
otherwise focus is the half that cannot be typed into.

Verified by construction rather than by a behavioural A/B: the framework's
focus state is not mirrored into the DOM and the canvas is not stable
between frames, so neither is observable from the browser harness. The
overlay suite stays at 14/14 with no page errors.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant