diff --git a/app-mochi/source/JihadBrowser.js b/app-mochi/source/JihadBrowser.js index 047c1fb..53fe9a6 100644 --- a/app-mochi/source/JihadBrowser.js +++ b/app-mochi/source/JihadBrowser.js @@ -1053,7 +1053,9 @@ enyo.kind({ if (done) { return; } done = true; window.jihadClip = null; - if (!enyo.jihad.setClipboard(String(text))) { enyo.jihad.toast("info", "The text could not be copied."); return; } + var set = enyo.jihad.setClipboard(String(text)); + enyo.log("[JIHAD-CLIP] copied " + String(text).length + " chars, clipboard set: " + set); + if (!set) { enyo.jihad.toast("info", "The text could not be copied."); return; } // True from this moment whatever the cut does; "Cut." replaces it if the engine // confirms the deletion (the "page-cut" notification). enyo.jihad.toast("info", "Copied."); @@ -1077,6 +1079,7 @@ enyo.kind({ var view = this.$.view; enyo.jihad.getClipboard(function(text) { var parts, i; + enyo.log("[JIHAD-CLIP] paste read " + (text ? text.length : 0) + " chars"); if (!text) { enyo.jihad.toast("info", "The clipboard is empty."); } else if (text.length > 262144) { enyo.jihad.toast("info", "The clipboard holds too much text to paste here."); } else { diff --git a/app-mojo/app/assistants/main-assistant.js b/app-mojo/app/assistants/main-assistant.js index 844207a..dd203aa 100644 --- a/app-mojo/app/assistants/main-assistant.js +++ b/app-mojo/app/assistants/main-assistant.js @@ -398,6 +398,25 @@ MainAssistant.prototype.adoptViewportModel = function () { this.metaViewport = new Mojo.WebView.MetaViewport(initialScale, minimumScale, maximumScale, width, height, userScalable); }; + // T-525 (the emulated finger, 2026-10-24): before the adapter acts on a tap or a hold it asks its + // eventListener — this widget assistant — `eventFired(event, info)` with the hit test; a true + // answer claims the event. Mojo.Widget.WebView has no such method, so every hold fell through to + // the engine's context event and a held link got the text commands only (Copy / Select All / + // Reload / Find on Page). As the Mochi shell's JihadWebView.eventFired: a hold on a link or an + // image is claimed and gets its own commands; a tap and any other hold fall through (claiming a + // tap would stop every tap on the page). + wa.eventFired = function (event, info) { + if (!event || event.type !== "mousehold" || !info || info.isNull) { + return false; + } + var link = info.isLink ? String(info.linkUrl || "") : ""; + var image = info.isImage ? String(info.imageUrl || "") : ""; + if (!link && !image) { + return false; + } + self.showHoldMenu({linkUrl: link, linkText: String(info.linkText || ""), imageUrl: image, altText: String(info.altText || "")}); + return true; + }; var at = function (event) { var node = wa.adapter, r = node.getBoundingClientRect(); return {x: Math.round(event.centerX - r.left), y: Math.round(event.centerY - r.top)}; @@ -929,6 +948,57 @@ MainAssistant.prototype.handlePageHold = function (text) { }); }; +//* The commands for a held link or image (T-525): a new card, a share, the address copied. +MainAssistant.prototype.showHoldMenu = function (held) { + var items = []; + this.held = held; + if (held.linkUrl) { + items.push({label: $L("Open in New Card"), command: "hold-open"}); + items.push({label: $L("Share Link"), command: "hold-share"}); + items.push({label: $L("Copy URL"), command: "hold-copy"}); + } + if (held.imageUrl) { + items.push({label: $L("Open Image in New Card"), command: "hold-open-image"}); + items.push({label: $L("Copy Image URL"), command: "hold-copy-image"}); + } + this.controller.popupSubmenu({ + onChoose: this.handleHoldChoice.bind(this), + items: items + }); +}; + +MainAssistant.prototype.handleHoldChoice = function (command) { + var held = this.held || {}, url; + if (command === "hold-open" || command === "hold-open-image") { + // Web addresses only, as window.open is: a page's file: or data: link must not reach a new card. + url = command === "hold-open" ? held.linkUrl : held.imageUrl; + if (!/^(https?|ftp):/i.test(url)) { + JihadToast.show("info", $L("Only web addresses open in a new card.")); + return; + } + this.openNewCard(url); + } else if (command === "hold-copy" || command === "hold-copy-image") { + url = command === "hold-copy" ? held.linkUrl : held.imageUrl; + try { + this.controller.stageController.setClipboard(url, false); + JihadToast.show("info", $L("Address copied.")); + } catch (e) { + JihadToast.show("info", $L("The address could not be copied.")); + } + } else if (command === "hold-share") { + this.controller.serviceRequest("palm://com.palm.applicationManager", { + method: "launch", + parameters: { + id: "com.palm.app.email", + params: {summary: held.linkText || $L("Check out this link..."), text: held.linkUrl} + }, + onFailure: function (response) { + Mojo.Log.warn("[Jihad] share failed: %j", response); + } + }); + } +}; + MainAssistant.prototype.handlePageMenuChoice = function (command) { switch (command) { case "page-select-all": diff --git a/context/kits/cavekit-mojo-ui.md b/context/kits/cavekit-mojo-ui.md index 68e50ef..750962a 100644 --- a/context/kits/cavekit-mojo-ui.md +++ b/context/kits/cavekit-mojo-ui.md @@ -80,7 +80,7 @@ Reference: `app-mojo/README.md`, `docs/IPC-CONTRACT.md`, the Mojo framework docs - [x] **Find** is in the row, as an icon (a magnifying glass), not a text item. *(Added 2026-08-05 on user report "there is no find UI", then "make find a magnifying glass instead of text": a text item is the one thing in the row that cannot be circular, and the row reads as a set of round controls.)* - [x] **Home** sits immediately right of forward and opens the configured home page. It is never disabled — unlike back/forward it does not depend on page history. *(Added 2026-08-05 by user request. Device-verified.)* - [x] The row **scrolls horizontally** rather than overflowing, so it can hold as many controls as the Enyo and Mochi shells. *(Device-verified 2026-08-05: 8 buttons, `client=768 scroll=768 overflowX=auto`.)* -- [~] **Long-press menu and padlock** (cavekit-ui-shell.md R7, R8): every hold goes to the engine (Mojo's web view claims none), and the card the user is in opens a `popupSubmenu` with the page's or the field's commands; the padlock is the address field's background on an https page, its tap answered with `showAlertDialog`. *(2026-10-06: driven from the Mojo test hook (`render/goanna/test/mojo-selftest.js`, `card-selftest.sh mojo`) on the device: a hold on page text opens Copy / Select All / Reload / Find on Page and Copy puts the word on the clipboard; a hold in a field opens Paste / Select All, Paste inserts the clipboard, Select All then adds Copy / Cut, and Cut leaves the text on the clipboard, the field empty and the engine saying "Cut."; the padlock tap on Wikipedia brings the Certificate alert. Found and fixed on the way: Copy and Cut had never reached the engine on Mojo — the WebView widget's `copy(callback)` hands an absent callback to the adapter as `undefined`, which the adapter refuses ("Bad arguments"), and the widget wraps no `cut` and no `pageFocused` at all. The shell now always passes a function to copy, and a method the widget does not wrap is called on the plugin element itself (R3).)* The finger on a link, and the hold's feel, are the owner's. [human at the device] — DEFERRED by the owner 2026-10-08 ("Defer additional finger tests for later") +- [~] **Long-press menu and padlock** (cavekit-ui-shell.md R7, R8): every hold goes to the engine (Mojo's web view claims none), and the card the user is in opens a `popupSubmenu` with the page's or the field's commands; the padlock is the address field's background on an https page, its tap answered with `showAlertDialog`. *(2026-10-06: driven from the Mojo test hook (`render/goanna/test/mojo-selftest.js`, `card-selftest.sh mojo`) on the device: a hold on page text opens Copy / Select All / Reload / Find on Page and Copy puts the word on the clipboard; a hold in a field opens Paste / Select All, Paste inserts the clipboard, Select All then adds Copy / Cut, and Cut leaves the text on the clipboard, the field empty and the engine saying "Cut."; the padlock tap on Wikipedia brings the Certificate alert. Found and fixed on the way: Copy and Cut had never reached the engine on Mojo — the WebView widget's `copy(callback)` hands an absent callback to the adapter as `undefined`, which the adapter refuses ("Bad arguments"), and the widget wraps no `cut` and no `pageFocused` at all. The shell now always passes a function to copy, and a method the widget does not wrap is called on the plugin element itself (R3).)* The finger on a link, and the hold's feel, are the owner's. [human at the device] — DEFERRED by the owner 2026-10-08 ("Defer additional finger tests for later") *(2026-10-24, the emulated finger (T-525): a held link got the text commands only — the Mojo widget has no `eventFired`, so the adapter's hit test (isLink, linkUrl, isImage, imageUrl) never reached the shell. Now `wa.eventFired` on the widget assistant claims link and image holds and `showHoldMenu` offers Open in New Card / Share Link / Copy URL (image: Open Image in New Card / Copy Image URL); Open in New Card verified. Text, field and other holds still go to the engine.)* - [x] The buttons are **circular and centred** as a group, with tight spacing. *(Device-verified 2026-08-05: 50x50 each, `btn0` at x=170 — (768-8*54)/2 — i.e. centred.)* - [x] **The other shells' upper-right menu, on the circles** (owner 2026-10-06: "the upper right menu has the same options on all 3 variants (see atlas for reference)", then "mojo should have no top toolbar icons next to url. bottom circular buttons only"). The **Share** circle opens Add Bookmark · Share Link (Email · Text Message · Copy Address) · Add to Launcher; the **Bookmarks** circle (it replaces the History circle) opens Bookmarks · History · Downloads; New Card stays a circle. Both are the framework's own command-menu `submenu` items, so the menu pops at the tapped circle. Bookmarks are the card's own capped list (`app/models/jihad-bookmarks.js`, no db8 kinds); Downloads lists what the engine saved (`app/models/jihad-downloads.js`, fed by the adapter's download callbacks). The address row carries nothing but the field. *(Device 2026-10-06, Mojo hook `Cshare`/`Clibrary` + `m`/`mN`: both menus, Add Bookmark → listed in Bookmarks, History and Downloads scenes.)* - [x] **Reload / Stop is inside the address field's right end**, not a circle (owner 2026-10-06: "put the mojo refresh icon embedded in the right of the urlbar instead of the bottom as a circular button"): the ink glyph (the field is white whatever the theme), Stop while loading. *(Device 2026-10-06, hook `J`: refresh → tap → stop while loading → refresh.)* diff --git a/docs/FINGER-TESTS.md b/docs/FINGER-TESTS.md index c9e3bc4..641b360 100644 --- a/docs/FINGER-TESTS.md +++ b/docs/FINGER-TESTS.md @@ -71,7 +71,7 @@ Run each item in all three variants (Enyo, Mochi, Mojo) unless it says otherwise bookmarks/history; every app-menu entry; Mojo's new-card / share / history. (device-build R4) *(2026-10-24, the emulated finger, T-525: Enyo — back, forward, reload (the stop glyph shows), stop, home (DuckDuckGo lite), share (Add Bookmark / Share Link / Add to Launcher), bookmarks panel, padlock (the DigiCert certificate, Done), app menu (Find on Page / Settings / Print), new card — all act. Mochi — the same set acts (clear, share, Bookmarks / History / Downloads, app menu, new card; back and forward after a link). Mojo — reload, home, new card, the library and share menus, find in page, the padlock dialog, the app menu, and back and forward (`pageBackward` loads the previous page, `pageForward` the next) all act. NB the bar's hit boxes, in card px at y 930-980 (screen y ≈ 982): back 197-247, forward 251-301, home 305-355, new card 359-409, library 413-463, share 467-517, find 521-571 — a tap at the back ICON's apparent position (265) lands on the disabled forward box and does nothing; measured with elementFromPoint through the daemon's load line, `docs/rounds/finger-2026-10-24/zoom-grey.txt`.)* - [~] Hold on text, on a field, on a link and on an image: the long-press menu offers the right entries and each does what it says; a plain tap is unaffected. (ui-shell R7, mochi-ui R2, - mojo-ui R6) *(2026-10-24, the emulated finger: Enyo — text: the word selected, Copy / Select All / Reload / Add Bookmark / Find on Page; field: the keyboard and Paste / Select All; link: Open in New Card / Share Link / Copy URL; image (the infobox picture, a linked image): Open In New Card / Share Link / Copy URL / Copy To Photos / Share Image / Set Wallpaper; a plain tap on a link still opens it. Mochi — text: the word selected and the page-hold notified; link: the sheet Hewlett-Packard / Open in New Card / Share Link / Copy Address / Cancel. Mojo — text: Copy / Select All / Reload / Find on Page; link: the SAME text menu, the word selected, although the daemon's hit test answers isLink (the shell's hold menu is built without the hit test's link and image facts — mojo-ui R6's link commands, open). The entries' actions (Copy to the clipboard, Open in New Card, Save) and Mochi's and Mojo's image holds not yet exercised.)* + mojo-ui R6) *(2026-10-24, the emulated finger: Enyo — text: the word selected, Copy / Select All / Reload / Add Bookmark / Find on Page; field: the keyboard and Paste / Select All; link: Open in New Card / Share Link / Copy URL; image (the infobox picture, a linked image): Open In New Card / Share Link / Copy URL / Copy To Photos / Share Image / Set Wallpaper; a plain tap on a link still opens it. Mochi — text: the word selected and the page-hold notified; link: the sheet Hewlett-Packard / Open in New Card / Share Link / Copy Address / Cancel. Mojo — text: Copy / Select All / Reload / Find on Page; link: at first the SAME text menu (the framework widget has no eventFired, so the adapter's hit test never reached the shell); FIXED the same evening — the shell claims link and image holds on the widget assistant (eventFired, as Mochi's JihadWebView does) and offers Open in New Card / Share Link / Copy URL (an image: Open Image in New Card / Copy Image URL); Open in New Card opened the held link in a second card. The entries' actions (Copy to the clipboard, Open in New Card, Save) and Mochi's and Mojo's image holds not yet exercised.)* - [x] Pick and dismiss an item in a card-side drop-down list; answer a card dialog by tap (an add-on install confirm, an SSL confirm). (device-build R4) *(2026-10-24, Enyo: `jihad-card-events.html`'s select opens the card's list (choose one / apple / banana / cherry), a tap on banana closes it and the page reads "picked: banana"; https://self-signed.badssl.com/ raises View Certificate / Trust Always / Trust Once / Don't Trust and Trust Once loads the page. The add-on install confirm not tried.)* - [~] about:settings: every control by finger at 1024×768, including the tabs (wrapped in Mochi) and