Repository navigation
feat(cli): tinyjs wrap <url> — one-line site wrappers - #20
Conversation
Scaffolds a wrapper project from any site: fetched title + best advertised icon (ICO containers get their inner PNG extracted), tinyjs.json with url + per-origin wrapper preset (host, subdomains, registrable domain — strangers deny-by-default), popups 'window', downloads 'ask', and a logging-only backend. --name / --ua / [dir]. Built .apps carry TinyjsUrl and run with no frontend.
A wrapped http://127.0.0.1:8123 stamps that exact origin — a hardcoded https:// key never matched, silently gating nothing for http sites. Origin key is now base.origin; wildcard subdomain keys stay https-only.
--menubar: activation accessory + generated backend sets hideOnClose, mounts a tray toggle and surfaces the window on bare tray click. --top: setAlwaysOnTop at init. Both are tinyjs APIs that already ship; the flags only compose them.
The generated backend's onNavigate policy hook answers 'external' for matches (exact host or subdomain, case-insensitive); everything else stays in-app — Nativefier's most-missed feature, on tinyjs's existing policy hook.
API_ALWAYS gains win.close/minimize/zoom/startDrag — a wrapped page
redirecting to a stranger origin (google.fr→consent.google.com,
slabb.dev→buymeacoffee.com, measured live) must keep its frameless
strip working; worst case for a hostile page is annoyance, never data.
wrap grows --force (regenerate a tinyjs project in place — the Studio's
edit flow) and persists menubar/top in tinyjs.json studio{} for form
restore; the menubar tray is icon-only.
--panel (with --menubar) generates a tray-click toggle that anchors a 360x520 frameless panel under the tray icon (app.tray.position + setPosition), always-on-top, and dismisses on outside click via onWindowState focus tracking. Implies frameless.
The wrapped site's own icon (fetched or picked) becomes the tray icon — template:false keeps favicon colors instead of macOS monochrome — with the globe symbol as fallback when no icon.png exists.
Fetched favicons are full-bleed and render oversized in the dock next to system apps. On macOS the icon is redrawn centered at ~82% of a transparent 1024 canvas via a small JXA + Cocoa helper.
|
Update — the wrap command grew into its final shape:
Verified on macOS 26: menubar/panel/top/external wraps built and run; icons padded; no-dots variants generate clean runtime-boolean injects. |
|
Thanks @slabbdev , I was going to fix myself but thought it would be better to have a second pair of eyes on it. Probably should change?1. The origin gate can open up to a whole public suffixThe registrable domain is taken as the last two labels, and The code comment says the cost of getting it wrong is "only strangers get no API, the fail-closed side". Here it's the other way round: anyone who can publish on that suffix gets
2. The
|
|
Thanks Tarwin — that's a proper review, and every point lands. Plan of attack, in order: the quick wins first ( The two meatier ones ( Thanks again for the time this took. |
…tches Review round 1 on tarwin#20: - --force overwrites only tinyjs.json / src/main.js / icon.png — user files, added files and .git survive; a re-wrap that finds no icon keeps the existing one; the generated src/main.js carries a hash stamp so a later --force warns when it was edited since generation - tinyjs.json no longer carries a "studio" key (Studio keeps its own file) - probe fetches cap and time out (page 10 s / 2 MB, icons 5 s / 5 MB) — a slow or hostile site neither hangs wrap nor feeds it an unbounded body - --flag=value works for every value flag; usage + help text list all flags and say --name <name>; --panel without --menubar says so
…ated gate Review round 2 on tarwin#20 (point 2): the runtime keeps API_ALWAYS at client.hello only, so gate policy stops changing for every tinyjs app — "api": "none" means none again. The generated config carries the chrome sugar at the top level instead: an origin that matches no key (a redirect target like consent.google.com) falls through to disable ["*"] + enable [win.close/minimize/zoom/startDrag], so a wrapped page keeps its window machinery on any origin while everything else stays closed. The wrap summary prints the gate that is actually written.
…ldcards Review round 3 on tarwin#20 (point 1): the generated gate is the exact origin only unless subdomains are asked for. The public suffix list — fetched at wrap time, cached ~30 days, exact-only fallback when neither is there — decides where a wildcard may stop: bbc.co.uk may widen to *.bbc.co.uk, while sam.github.io or myapp.vercel.app (tenants on a hosting suffix) and every IP or http origin stay exact. Non-interactive by default: --origins exact|subdomains|url,… plus --yes; on a TTY wrap shows the two choices and defaults to exact on enter. IP hosts get a sane ip-*.wrap bundle id, and the summary prints the origins actually written. The PSL's own section markers sit inside comments — parsed before the comment strip, or the private section (github.io, vercel.app) is never seen and hosting-suffix tenants get wildcards (caught by the sam.github.io test). The interactive prompt is TTY-gated and needs a real terminal to exercise; undeclared stdin falls back to exact.
|
Pushed since the plan above — points 1 and 2 of your review are done on this branch:
Tested against the live list (8 scenarios, fresh dirs each): bbc.co.uk widens; the tenants, IPs and default runs stay exact. One honest gap: the interactive prompt needs a real terminal to exercise — the isTTY gate keeps pipes non-interactive (exact). Remaining from your review: #5 |
wrap fixes on top of the merged PR: the menu-bar tray icon ships as src/tray.png and resolves next to the backend module (a packaged app runs from / without the project's icon.png); the page title is cut at a tagline separator and stripped of filename-unsafe characters (it becomes the .app path); --force carries over unflagged choices (origins, ua, window modes, external list, user-owned keys), adds --no-menubar/--no-panel/--no-top, and backs up an edited src/main.js to src/main.js.bak. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Scaffolds a wrapper project from any site: the
"url"config makes the site the main window, the per-origin API gate ("wrapper"preset: exact host,*.subdomains, registrable domain — strangers deny-by-default) keeps the third-party origin away from the machine,popups: "window",downloads: "ask", and a logging-only backend. No frontend./favicon.icofallback; ICO containers get their inner PNG extracted (sips refuses an ICO namedicon.png)--name,--ua, optional[dir]argumentTested on macOS 26: Hacker News + GitHub wrapped;
tinyjs devboots and traces the full NAV pipeline through the wrapper backend;tinyjs buildproduces codesigned .apps carryingTinyjsUrl/TinyjsPopups/TinyjsDownloads(verified in the plists). The Windows/Linux legs consume the shipped browser-affordances unchanged — not exercised on hardware here.A consumer example (TinyJS Studio — a desktop UI driving new/wrap/dev/build) is at https://github.com/slabbdev/tinyjsapp-studio