Previously the dashboard used a clean root URL for the 'apps' tab and hashes for other tabs. This change makes tab links and history entries consistently use a hash (e.g. #apps, #home) by always generating href as `#<id>` and pushing `#<section>` to history. Simplifies URL handling and ensures the Apps tab also reflects its hash in the address bar.
* feat: add custom web link icon support and context menu option
Introduce the ability to change and display custom icons for .weblink files. Added a "Change Icon" context menu item that invokes the changeWeblinkIcon helper, and updated the icon rendering logic to use getWeblinkIcon instead of the default link icon. This allows users to personalize web link appearance in the file manager.
* Fix security and robustness issues in web link custom icons
# UIWindowSearch and require the icon data URL body to be pure base64 in
#isValidWeblinkIcon. A shared/downloaded .weblink is untrusted input and
# the previous prefix-only check let a crafted icon break out of the
# <img src> attribute.
# byte-signature MIME sniffing and capping stored icon size (was storing
# full-resolution images as base64).
# without picking a file.
# weblinks no longer fetches every file's contents on each render.
# path instead of the target weblink).
# label, add the AGPL license header, and drop redundant stored icon
# copies.
* Keep SVG weblink icons as vectors instead of rasterizing
Rasterizing a picked SVG to a 256px PNG threw away its scalability for no
real benefit: an SVG rendered in <img> runs in script-free static mode, and
the base64-body validation already prevents attribute injection. Store SVGs
as data:image/svg+xml;base64 (content-sniffed, size-capped), and keep
rasterizing raster formats and oversized SVGs to bound stored size.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
---------
Co-authored-by: Daniel Salazar <daniel.salazar@puter.com>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
The global ctrl/cmd+f shortcut opened UIWindowSearch and preventDefault'd
the keystroke, blocking the browser's native find. Skip that interception
while a dashboard window is open so ctrl/cmd+f falls through to the browser.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Treat the Apps tab as the default (root URL) instead of Home. UIDashboard now reads an initial route from window.dashboard_initial_route?.tab and falls back to 'apps' for unknown/absent routes. Sidebar and section active class logic and tab href/hash generation were updated so 'apps' uses the clean root URL while all other tabs (including home) use #tab. The history.pushState logic was adjusted accordingly. parseDashboardRoute in initgui.js was updated to default to 'apps' and its comment clarified the new behavior.
Update .myapps-tile-icon in src/gui/src/css/dashboard.css to change width and height from 60px to 56px, adjusting tile icon sizing for improved layout and spacing.
Call focusSearch when the Apps dashboard is activated and add a focusSearch helper. The helper imports isTouchPrimaryDevice and skips focusing on touch-primary devices to avoid showing the on-screen keyboard. It retries up to 12 times (30ms interval) until the search input is visible to handle cases where the dashboard is briefly hidden while entering full-page mode.
Update .myapps-tile-icon width and height from 52px to 60px to improve icon visibility and better match the updated UI design. Other styles (overflow, background, transitions) are left unchanged.
When an app is external and provides an index_url, use its hostname as the tile title and store the index_url in a new data-target-link attribute. Update the click handler to open the target link in a new tab for external apps, and fall back to opening the internal /app/{name} page for others. This aligns Apps tab behavior with the Home tab and ensures external apps navigate to their website.
* Mark external apps and use the flag for dashboard titles
An app with no owner_user_id (null/empty) isn't owned by a Puter user —
it's an external, origin-bootstrapped app whose uuid/name/title are all the
opaque app-… id. The dashboard used to detect these client-side by comparing
uuid/name/title and a name.startsWith('app-') check.
Expose an authoritative `external` flag from the API instead:
- /installedApps and /get-launch-apps now return `external`, derived from
owner_user_id, and no longer leak the raw owner id.
- The dashboard (Home + Apps tabs) shows the index_url hostname for external
apps based on `external` rather than the uuid/name/title heuristic.
Add/extend extension tests to cover the `external` flag and non-leak.
* Use hostname for opaque external app titles
Detect opaque external app IDs (when name === title === uid/uuid) and replace the displayed title with the hostname from index_url. Adds a uid/uuid fallback and tightens the external-app check in both the Apps and Home dashboard tabs so they behave consistently; preserves target_link for Home entries.
* Add external flag for apps without owner
Expose an `external` property in AppSummary (toAppSummary in RecommendedAppsService.ts) to mark apps that are not owned by a Puter user. The flag is set when `owner_user_id` is null or an empty string, allowing clients to identify origin-bootstrapped/external apps.
The dashboard derives a hostname title for anonymous (app-…) apps whose
uuid/name/title are all identical, falling back to the host of index_url.
That worked for launch/recent apps but not for apps that come only from
/installedApps, because the endpoint didn't return index_url.
Add apps.index_url to the SELECT (alongside apps.icon, relying on the
functional dependency on the apps.id primary key) and assert it in the
extension test.
Detect anonymous apps whose name/title/uid are identical opaque ids (starting with 'app-') and, when index_url is available, replace the displayed title with the index_url hostname (matching the Home tab behavior). Also persist index_url into the mapped app object and make title a mutable variable to support the change.
* feat: make the dashboard the default interface at root
The dashboard now loads at / (with /dashboard kept as an alias), and the
desktop moves to a new /desktop route. An "Open Desktop" button pinned at
the bottom of the dashboard sidebar switches to the desktop.
Root URLs that carry a desktop-only flow keep booting the desktop:
auth popups (?embedded_in_popup=), app deep links (?app=), direct
downloads (?download=), fullpage mode (?puter.fullpage=), and iframe
embeds.
Desktop-context redirects are retargeted so desktop users stay on the
desktop: logout, the QR "continue on phone" link, the auth cover-page
logo click, and window close/hide URL resets now use /desktop. OIDC
login/signup gains a strictly whitelisted return_to param (/desktop or
/dashboard) so social login returns to the interface it started from.
Also: the dashboard strips ?ref= like the desktop does, ?auth_token=
cleanup preserves the path and tab hash, the logged-out session list
gets the wallpaper back, ?download= also works at /desktop, and the GUI
dev server serves /desktop and /dashboard.
* Add Open Desktop bento card and CSS
Replace the previous desktop-switch UI with a full-width "Open Desktop" bento card that links to /desktop. Removes the pinned sidebar Open Desktop button and the old desktop-switch button/handler, and adds new CSS variables and styles for the desktop card (icon background, shadow, surface/header colors), hover shadow and arrow transition, and layout/padding adjustments.
Changed files: TabHome.js (removed old switch, added anchor card), UIDashboard.js (removed sidebar button), dashboard.css (new desktop color vars and styles; removed legacy desktop-switch/button styles).
* Remove box-shadow from myapps search input
Delete the box-shadow declarations from input.myapps-search::placeholder in dashboard.css. This simplifies the placeholder styling (removes subtle and light shadow layers), likely to reduce visual noise or fix rendering inconsistency.
* Show index_url hostname for opaque app IDs
Only replace an app's title with the hostname from index_url when the app appears to use an opaque id. Add a check for names starting with 'app-' so the hostname is used only if title, name and uuid are identical and the name looks like an opaque app id. This avoids overwriting meaningful user-facing names that may coincidentally match the uuid and clarifies the intent in the comment.
* Update dashboard.css
Include 'contacts' in the RECOMMENDED_APP_NAMES array so the Contacts app appears in the recommended apps list. Updated src/backend/services/apps/RecommendedAppsService.ts.
* fix(docs): highlight all code blocks after client-side navigation
The docs site highlights code on full page load via an inline
hljs.highlightAll(), which targets the 'pre code' selector and so
highlights every fenced block, including those without a language tag.
The client-side navigation path (pathchange handler) instead selected
only code[class^="language"], so fenced blocks written without a
language hint were left unhighlighted after in-app navigation while
appearing correctly on a hard reload.
Align the pathchange handler with highlightAll() by selecting 'pre code'
and skipping blocks hljs has already processed (data-highlighted=yes),
keeping the pass idempotent across repeated pathchange events.
Fixes#3287
* fix(docs): hoist hljs.configure and scope highlight to docs content
Address review nits on the pathchange highlight pass:
- Move hljs.configure({ ignoreUnescapedHTML: true }) out of the per-element
loop since it is global config and only needs to run once.
- Scope the selector to '.docs-content pre code' so the re-highlight pass
only touches the swapped-in docs body and leaves code blocks elsewhere
on the page untouched.
Adjust RECOMMENDED_APP_NAMES ordering: place 'editor', 'camera', and 'recorder' immediately after 'builder' and move 'app-center' accordingly. Also add 'vault' to the recommended apps list. This changes the resolved ordering of recommended apps at call time.