feat: add naked link - #65
Draft
leoafarias wants to merge 16 commits into
Draft
Conversation
Contributor
LCOV of commit
|
Contributor
LCOV of commit
|
Contributor
LCOV of commit
|
Contributor
LCOV of commit
|
# Conflicts: # .github/workflows/ci.yml # .github/workflows/integration-tests.yml # packages/example/integration_test/screenshot_smoke.dart # packages/example/lib/registry.dart # packages/example/test/accessibility_guidelines_test.dart # packages/example/test/screenshot_evidence_test.dart # packages/naked_ui/CHANGELOG.md # tool/run_android_integration.sh
Contributor
LCOV of commit
|
Contributor
LCOV of commit
|
Contributor
There was a problem hiding this comment.
Pull request overview
Adds a new headless NakedLink primitive to naked_ui, plus supporting intents, hover/focus utilities, documentation, examples, tests, and CI/integration evidence updates.
Changes:
- Introduces
NakedLink,NakedLinkState, andNakedLinkResolverwith Link semantics and Enter-only keyboard activation. - Extends
NakedFocusableDetectorto optionally restore hover when re-enabled under a stationary pointer, and stabilizes Actions/Shortcuts wrappers across enable/disable changes. - Adds extensive unit/semantics/integration/golden evidence and updates CI workflows to validate Link behavior and artifacts.
Reviewed changes
Copilot reviewed 30 out of 32 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tool/run_android_integration.sh | Verifies new Android Link screenshot evidence artifacts exist after the run. |
| README.md | Documents the new NakedLink API and resolver usage at the workspace level. |
| pubspec.lock | Adds url_launcher transitive resolution and updates recorded Dart SDK minimum. |
| packages/naked_ui/test/utilities/naked_focusable_detector_test.dart | Adds coverage for hover restoration behavior across disable/enable transitions. |
| packages/naked_ui/test/src/naked_link_test.dart | New widget tests for link activation, modifiers, lifecycle, focus, state, and platform-default navigation. |
| packages/naked_ui/test/semantics/naked_link_semantics_test.dart | New semantics contract tests ensuring Link role/URL/actions match expectations and Button semantics are absent. |
| packages/naked_ui/test/hashcode_contract_test.dart | Extends hashCode contract tests to include NakedLinkState. |
| packages/naked_ui/README.md | Package-level documentation updates for NakedLink and resolver behavior. |
| packages/naked_ui/pubspec.yaml | Adds url_launcher dependency needed for native/web Link coordination. |
| packages/naked_ui/lib/src/utilities/naked_focusable_detector.dart | Adds opt-in hover restoration and stabilizes Shortcuts/Actions wrappers when disabled. |
| packages/naked_ui/lib/src/utilities/intents.dart | Introduces Link-specific intent/shortcut mapping for Enter/Numpad Enter without Space. |
| packages/naked_ui/lib/src/naked_widgets.dart | Exports naked_link.dart from the public widgets barrel. |
| packages/naked_ui/lib/src/naked_link.dart | New NakedLink implementation with semantics, resolver routing, and url_launcher-backed navigation. |
| packages/naked_ui/lib/src/mixins/naked_mixins.dart | Adds optional rebuild control for state synchronization during framework lifecycle callbacks. |
| packages/naked_ui/CHANGELOG.md | Changelog entry describing the new Link primitive and related behavior updates. |
| packages/example/test/screenshot_evidence_test.dart | Ensures Link evidence artifact names remain stable/deterministic. |
| packages/example/test/naked_link_example_test.dart | Tests the example fixture behavior/state determinism and semantics expectations. |
| packages/example/test/goldens/golden_test_harness.dart | Updates golden font loading to include pinned Material Icons for deterministic rendering. |
| packages/example/test/goldens/components/naked_link_golden_test.dart | Adds a Link golden test (keyboard focus baseline on pinned Linux host). |
| packages/example/test/accessibility_guidelines_test.dart | Adds accessibility guideline checks for inline and standalone Link fixtures. |
| packages/example/test_driver/link_browser_driver.dart | Adds a web-driver test to verify trusted-event browser ownership and navigation behavior. |
| packages/example/pubspec.yaml | Adds dev deps for flutter_driver, webdriver, and web to support the new browser driver flow. |
| packages/example/lib/registry.dart | Registers the new Link demo in the example demo registry. |
| packages/example/lib/api/naked_link.0.dart | Adds the Link example fixture(s) used by tests, goldens, screenshots, and guidelines. |
| packages/example/integration_test/screenshot_smoke.dart | Adds Link screenshot capture scenarios (inline, focus, disabled, long text) and surface checks. |
| packages/example/integration_test/link_browser_driver_app.dart | Adds a minimal app target for flutter drive browser-ownership verification. |
| packages/example/integration_test/components/naked_link_integration.dart | Adds integration tests for focus/activation, Space non-activation, semantics, disabled behavior, and RTL/long-text robustness. |
| packages/example/integration_test/all_tests.dart | Registers Link integration tests into the suite runner. |
| .github/workflows/integration-web.yml | Runs and verifies the new web flutter drive browser ownership log evidence. |
| .github/workflows/integration-tests.yml | Verifies new macOS screenshot evidence artifacts exist in CI. |
| .github/workflows/ci.yml | Improves golden failure diagnostics by generating/uploading candidate images only on the pinned-host failure path. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comment on lines
+248
to
+252
| if (kIsWeb && widget.linkUrl.hasScheme) { | ||
| unawaited(_launchWebExternalLink()); | ||
| return; | ||
| } | ||
| unawaited(followLink()); |
Comment on lines
+283
to
+291
| void _handlePointerActivation(launcher.FollowLink followLink) { | ||
| final modified = _modifiedPointerActivation || _hasPointerModifier; | ||
| _modifiedPointerActivation = false; | ||
| if (modified) { | ||
| unawaited(followLink()); | ||
| return; | ||
| } | ||
| _handleOrdinaryActivation(followLink); | ||
| } |
leoafarias
marked this pull request as draft
July 14, 2026 00:59
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds the headless
NakedLinkprimitive from the Phase 2 contract. The Link exposes Link rather than Button semantics, optional URL metadata, effective-disabled behavior, observable hover/focus/press state, caller-owned focus-node support, primary/semantic activation, and Enter/Numpad Enter keyboard activation. Space and secondary click remain unclaimed.This PR is ready for code review, but Phase 2 closure is explicitly blocked on the evidence listed below. Do not merge without maintainer authorization.
1. Contract summary
NakedLinkand immutableNakedLinkState.enabled && onPressed != null.linkUrl, name/hint, enabled/focus state, and tap only while enabled; Button semantics are absent.2. Compatibility statement
This is an additive, non-breaking API. No existing public API, source behavior, semantics, keyboard mapping, or focus behavior changes. Consumers adopting
NakedLinkshould replace text-styled navigation buttons only when Link semantics and Enter-without-Space activation are intended.3. Implementation notes
WidgetStatesMixin,NakedStateScopeBuilder, andNakedFocusableDetector.ActivateIntentbehavior.4. Requirement traceability
naked_link_test.dart; hash contractnaked_link_semantics_test.dartTest-first proof included missing-API compilation failures, missing-Link semantics failures, and targeted failing mutations for Space activation, secondary activation, Button semantics, target size/contrast, inline height, and standalone width. All temporary mutations were restored.
5. Screenshot manifest
Exact implementation/evidence head:
24460f0a94b657854c95d5dc900e5ef7215d9604; hosted merge ref09e62c8dc29b424a1d00e5e7de8cfc4a99cd124f. Local macOS captures use Flutter 3.41.2, 800×600 logical pixels, DPR 1, en-US/LTR, and disabled animations.link__default_inline__macos__reference.png02f2565b397780d8ea50d2a8c98c754fcab85464d13abe76c8b962097fd3a6a7link__keyboard_focus__macos__reference.png18683fbc41e260fa54622db65893c164bd4bdb29b06cbdea32754a098c9d9095link__long_text_200__macos__reference.png4bf12ea4007dadc972a74bb71fa9a376f85dc823aca53658d381a70a68085cdalink__disabled__android__reference.pngdb651e38a3e4b5f53e4ae07cd059e02a235b29563ea222806ce96795b5636b75link__hover__web__reference.pnglink__external_hint__web__reference.pnglink__rtl__web__reference.pngVisual review: exact manifest metadata and file dimensions verified; default inline layout, visible keyboard focus, standalone target sizing, disabled styling, 200% wrapping, clipping/overflow, and Android safe-area placement were reviewed. The accepted Ubuntu golden is 800×600 with pinned Roboto and Material Icons, SHA-256
88f39adcc2a5916f370d2ed5fdd8ff897e9c6cc4f00a5e3a9d71152ccf22086e; the same hosted job subsequently compared the checked-in file unchanged. No overlay behavior applies to Link.6. Accessibility evidence
7. Platform evidence
Local Flutter 3.41.2 evidence before publication:
dart format --set-exit-if-changed .— pass.flutter analyze— pass.flutter-tester— 8 pass.flutter-tester— 96 pass, 1 documented Tooltip skip.All seven hosted checks passed on implementation head
24460f0/ merge ref09e62c8:flutter-tester, real macOS, and screenshot artifact.Final documentation-only head:
261455521a27fb822307f7ff83bd1082725c5ecc; final test merge ref:fab2edadfeee84e07fd44df2a91f8884df7451c7. All exact-head checks are green:flutter-tester, real macOS, and screenshot transport pass.All tests passed.8. Known limitations and blockers
9. Documentation
Includes API dartdoc and example, root/package README usage, registry entry, changelog, executable Phase 2 plan, status board, test map, evidence names, and compatibility/non-goal notes. No migration is required because the API is additive.
10. Consumer note
Remix should provide routing or launching in
onPressed, passlinkUrlonly as semantic/DOM metadata, localize labels and hints, and style the builder's immutable states. Remix must still verify its router behavior, modifier-click policy, visited appearance, standalone versus inline target sizing, and later Context Menu/Hover Card composition.Checklist