v0.2.8
Pre-release
Pre-release
·
41 commits
to main
since this release
Axis Browser v0.2.8
Privacy / browsing
- Dependency
ip-address(CVE-2024-42338):npmoverridespinsip-addressto^10.2.0(transitive viasocks/ dev tooling) so XSS in Address6 HTML-emitting methods is patched; lockfile resolves10.2.0. - Dependency
fast-uri(CVE-2024-6321 + host-confusion advisory):npmoverridespinsfast-urito^3.1.2(transitive viaelectron-builder/electron-store/ajv) so path traversal via percent-encoded dot segments (GHSA-q3j6-qgpj-74h6) and host confusion via percent-encoded authority (GHSA-v39h-62p7-jpjc) are patched; lockfile resolves3.1.2. - Ad blocker: Network and cosmetic blocking via
@ghostery/adblocker-electron(cached lists under user data). Toggle with the shield in the URL bar (left of copy link) or Settings → General → Ad blocker; default is on. - Guest context menu — Save Image: Right-click on an image includes Save Image; it runs
webContents.downloadURLon the guest<webview>(save-image-from-url) so the normal download flow and session cookies apply (with shell fallback if the guest id is unavailable). The in-app#webpage-save-imagerow matches for any HTML fallback menu. - Guest context menu — image open / copy: Open Image in New Tab, Copy Image, and Copy Image Address work for relative and safe
data:image/*URLs (resolved againstfile:,axis:, http(s), etc., whenever the page URL parses); SVG and AVIF data: URIs are included. New tabs use a trusted image navigate path sodata:is not dropped bysanitizeUrl. Copy Image uses maincopy-image-at-guest(webContents.copyImageAt) with the menu’s guest id and coordinates, with<webview>.copyImageAtas fallback; the native menu IPC forwardspageURLandguestWebContentsIdwhere needed. Copy Image Address useswriteTextToClipboard(navigator.clipboard→execCommand('copy')→write-clipboard-textin main) so copying works after a native context menu (no transient user gesture). - Extensions: Install from the Chrome Web Store or Mozilla Add-ons (addons.mozilla.org) via the blue Install in Axis bar on listings, the URL bar install button, or Settings → Extensions (paste Chrome URL/ID, Mozilla URL, or Firefox slug e.g.
ublock-origin). Store-triggered.crx/.xpiguest downloads are cancelled; Axis fetches via Google update or Mozilla’s API instead. Firefox.xpipackages unpack like Chrome zips — Firefox-only APIs may still prevent some add-ons from running in Chromium. After install succeeds, the banner hides for that listing. Fallbacks:.crx, or an unpacked folder withmanifest.json. Settings → Extensions lists, enables/disables, removes, and opens options when provided. The URL bar puzzle button opens the extensions menu (frosted downloads-style popup). Toolbar extension popups load directly as top-levelchrome-extension://…pages in the extension session (no data-URL<webview>wrapper) withcontextIsolation: falseand a preload shim for missing toolbar/window APIs. Manifest V3 installs now load through a generated runtime compatibility copy that mapsaction→browser_action,host_permissions→permissions, andbackground.service_worker→ an MV2-style background page/scripts with an API shim, so popup UIs like Dark Reader and Chessvision have a running background context in Electron. The compatibility manifest strips MV3-only / unsupported fields (scripting,fontSettings,contextMenus, content-scriptworld) including emptyoptional_permissions, and usesaxis_extension_api_shim.js(not a reserved_filename). Extension loading usessession.extensions.loadExtensionwhen available; expected MV2 deprecation warnings for generatedextension-runtimecopies are suppressed, and ad blocker setup ignores Ghostery’s duplicate cosmetic-filter IPC registration after the first successful enable. - Downloads popup: While a file is still downloading, the per-row control next to the entry is a cancel (×) that calls
DownloadItem.cancel()viacancel-active-downloadinstead of the greyed-out show in folder icon; completed rows still use reveal in folder. The footer Open Downloads button is unchanged. After cancel, the row shows Download cancelled until you close the popup (avoids a fake just now time from the partial file on disk).
Theme / OS appearance
- macOS menu — View: Enter Full Screen at the bottom uses ⌃⌘F with a custom
setFullScreenaction (notogglefullscreenrole) so it does not duplicate the system-injected full-screen row. Windows/Linux keep F11. - macOS window hairline: Removed custom
--axis-window-chrome-radius/#appcorner clip (it didn’t matchNSWindowand read as a rim). Darwin drops the bodytranslateZ/perspectivepromotion (subpixel vs mask) and hides the grain::after(mix-blend-modedarkened the bezel).BrowserWindowshape is Electron default (roundedCornersno longer forced inAXIS_MACOS_BROWSER_WINDOW_SHAPE— traffic lights only). - Glass theme tint:
getThemeAwareGlassAlphacaps pale-theme bumps bywindowChromeLightso airy values stay translucent, but adds chroma insurance (lift × HSL saturation + a higher baseline floor) so light blue/pink/etc. still read as the chosen color.approximateGlassSurfaceHexdefault ambients for bright themes aremixHexColors(theme, cool light)so small-tintcontrast math isn’t a flat grey wash. Heavier slider positions still thicken the veil;--shell-ink-rgbwiring unchanged. - OS light/dark must not restyle Axis: Electron’s
nativeTheme.themeSourcedefaulted tosystem, so switching the computer to Light changedprefers-color-scheme, vibrancy, and native form chrome and made the shell look wrong. The app now pinsnativeTheme.themeSourcetodarkso appearance stays stable; Settings → Theme → Appearance (light overlays /settings.html) is unchanged. - Settings as a browser tab: Settings (
axis://settings) now opens as a normal sidebar tab (gear icon, closeable, ⌘W / undo) instead of a separateBrowserWindow. The tab loads embeddedsettings.htmlwith a dedicated guest preload (contextIsolation: true,contextBridge, same IPC surface as the old window); menu bar / ⌘, / extension shortcuts focus or create the single Settings tab and can jump to a section (History, Shortcuts, Extensions, etc.). History, shortcuts, site permissions, and extensions refresh when their pane is opened. The in-window title strip is hidden in embedded mode. - Settings tab data sections: History, Shortcuts, Site permissions, and Extensions now load store-backed lists in the tab webview — the settings guest always runs JavaScript (even when Disable JavaScript is on for pages), uses the settings preload when the webview is recreated, and retries IPC bootstrap after load without host-side control polling.
- Settings tab URL bar: On
axis://settings, the top bar now follows Settings → Theme → Appearance (light / dark), matching the new tab page shell instead of the user theme color. - New tab URL bar (light mode): Fixed the top bar staying dark in light appearance —
#webview-url-barno longer reuses the overlay classnew-tab-page(that rule forcedbackground: transparentand showed the dark shell through). Uses dedicatedurl-bar-internal-shellstyling with an explicit whitebackgroundinstead. - Settings tab blank page: Fixed
switchToTabtreatingaxis://settingshistory as a loaded guest and skippingsettings.htmlload, which left an empty white webview on open or tab switch. - Settings tab interactions: Shortcuts and Extensions controls no longer get trapped in a render/section-refresh loop when the Settings search box is empty, so shortcut recording/toggles and extension buttons respond normally.
- Settings page bootstrap (
settings.html): Theme was effectively “light until async settings load” and only applied whenuiThemewas already defined in the store, so it tracked the default stylesheet (and felt like the OS). A syncgetSettingsWindowBootstrapIPC now readsuiThemefromelectron-storebefore first paint; missinguiThemeis treated as dark like the main window. Basehtml/body/color-schememeta defaults match dark so there is no light flash. - Standalone Settings visual refresh:
settings.htmlnow uses a strict monochrome shell that follows the in-app light/dark setting: white surfaces in light mode, black surfaces in dark mode, neutral selection states, roomier cards/rows, improved search, buttons, inputs, extension rows, and responsive stacked navigation while preserving existing behavior. - Selection “Ask” (highlight) + floating Ask sheet:
.ai-selection-button,#ai-popup(input, send, response) now follow:root[data-ui-theme]like the rest of Settings → Theme → Appearance, instead ofbody.light-theme(which was never set).
Settings — Extensions
- Pane layout: Browse stores (buttons open Chrome Web Store and Firefox add-ons in Axis), Install from URL or ID, Install from disk, and Installed list.
- Menu bar (Extensions): The system menu adds Extensions (left of Window): installed extensions show manifest icons where available; rows stay enabled whenever the extension is not turned off in Settings (only disabled extensions are greyed). Choosing one opens its popup or options when present; otherwise an explanatory dialog appears. Add extension (Chrome Web Store) / Add extension (Firefox Add-ons) open store browse pages in the focused browser shell when it is active (otherwise the main window), followed by Manage Extensions… with no extra separator between those actions. The list refreshes when extensions are installed, removed, or toggled.
Settings (copy)
- Window transparency:
windowChromeLightblends opaque → airy endpoints (AXIS_SHELL_CHROME_OPAQUE/AXIS_SHELL_CHROME_TRANSPARENT). The airy endpoint alphas/blur/sat were lowered again so 100 admits more desktop light; above 0 the blend uses1 − (1−t)^1.85so mid–high slider values lean further toward airy without widening the widget range. - Transparent sites (glass mode): BETA badge moved here from Window transparency — glass / page-background clearing is the experimental surface; window chrome transparency is treated as stable copy.
Security
- CodeQL settings section navigation: Settings tab section focus no longer builds guest
executeJavaScriptfrom interpolated section ids — sections are allowlisted and sent overswitch-settings-tabIPC (webview.send) instead. - CodeQL URL checks: Context-menu image navigation uses full scheme validation (
data:only when_isSafeContextMenuDataImageUrl); sidebar media dock YouTube styling usesisYouTubeHost(parsed hostname) instead ofyoutube.comsubstring checks.
New tab page
- Recover closed tab (⌘Z):
sanitizeUrlnow treatsaxis://newtab,axis://settings, andaxis:note://…as first-class internal URLs instead of sending them throughgetSearchUrl, so undo / recover reopen the real new tab (not a Google search foraxis://newtab). - Enter on “Search or enter a URL”: Bare links (e.g.
example.com,www.site.org/path) still resolve withsanitizeUrl; any text with spaces that does not start withhttp://orhttps://is treated as a search so normal queries are not turned into odd%20paths. - Gray translucent background: After removing the idle webview well, a transparent NTP let the shell read gray instead of black.
#new-tab-pageis#000000whentransparent-sites-modeis off (default dark glass-off). Settings → Theme → Appearance → Light still uses a transparent NTP viadata-ui-theme="light". - Transparent sites ignored on NTP:
body.transparent-sites-mode .new-tab-pagehad been set to#000for the gray fix, so glass mode no longer showed through. That override istransparentagain; frosted search / toggle / ask cards were already wired to--axis-nt-*vars.
Sidebar
- Favorites: Favorites row above pinned tabs (Add to Favorites in the tab menu). Equal-width cells (
auto-fit+minmax+1fr) so each row spans the sidebar; fixed row height, flat fills; active tile and the sidebar profile switcher use--shell-ink-rgb(same as tabs / New Tab) so they stay readable on any shell theme and are no longer forced to light-mode black overlays whenuiThemeis light; highlight tracks current tab (re-synced on NTP / internal URLs). Standalone runtime tab per favorite; drag to reorder or drag out to remove. Right-click opens a native context menu (show-favorite-context-menu) — Open, Open in New Tab, Copy Link, Rename…, Change Icon / Reset Icon, Remove from Favorites — instead of instantly deleting the tile. - Clear unpinned skips favorites: Clear (separator + floating) no longer closes favorite runtime tabs (
isFavoriteTab), so using Clear while a favorite is focused does not tear down that session or corrupt favorite state. After Clear,_forceGuestLayoutSyncruns so the active guest picks up the real#webviews-containerwidth (fixes responsive pages stuck in a narrow column beside an empty panel when switching to a favorite with no sidebar row). The same layout flush now runs after closing the last normal tab when focus lands on a favorite runtime tab (_syncAfterTabClose), and favorite switches defer the flush until afterupdateEmptyStatesettles. - Favorites layout after tab close: Each favorite now owns a hidden sidebar tab host (same focus/layout path as pinned tabs) plus deferred guest bounds sync when reactivated, so closing the last normal tab no longer leaves the favorite page in a narrow column. Returning to a favorite does not reload the guest (
getURL()while inactive no longer reassignssrc; layout rebind runs only when the viewport is still stale after paint). - Clear unpinned icons: Separator and floating Clear controls now use
fa-arrow-downat 9px (wasfa-chevron-downat 7px, which read as a cropped chevron). - Profile switcher (sidebar footer): Single icon trigger at the bottom left of the sidebar (
sidebar-footer-profile-row), below the mini player (shows current profile only:fa-user/fa-user-secret). Clicking opens a compact menu to pick Personal or Incognito; the menu opens upward. Behavior is unchanged: clicking the current profile does nothing; choosing Incognito from personal first focuses an existing incognito window, and only creates one if none exists. Removed the large in-sidebar Incognito banner. - Profile switcher interaction: Sidebar profile trigger/menu now explicitly use
-webkit-app-region: no-drag(and pointer-enabled layering), so clicks are no longer swallowed by the sidebar drag region and the menu is fully interactive. - Profile switcher (incognito → personal): Personal selection now focuses an existing personal window or creates one if none exists, without forcing
open-url-in-browsernavigation (so it no longer spawns an unexpectedgoogle.comtab). - Clear unpinned (no pinned state): Floating Clear (when the separator is hidden) sits in
sidebar-tabs-topbaron the traffic-light side (top: 11px); still fades in on sidebar hover and callsclearUnpinnedTabs. - Clear unpinned visual parity: Floating no-pinned Clear now uses the same shell-ink opacity states as separator Clear (
0.38default,0.88hover,0.96active) so both controls match exactly. - Clear unpinned in incognito: Floating no-separator Clear visibility now updates from
updateEmptyStateas well, so it appears correctly in incognito windows where pinned-save flows are bypassed. - Clear unpinned in incognito (contrast): Added incognito-specific floating Clear color overrides (
rgba(255,255,255, 0.38/0.88/0.96)) so it remains visible on the pure-black incognito sidebar. - Extension store install bar recovery: Added a renderer fallback preload path (
webview-preload-bundle.jsresolved fromindex.html) when IPC preload-path lookup fails, so Chrome Web Store / Mozilla Add-ons “Install in Axis” banner injection still runs. - Extension store install bar fallback (host): Added a host-rendered purple Install in Axis bar in
.webview-containerthat appears on Chrome Web Store / Mozilla Add-ons listing URLs and uses the same install flow as the URL-bar install button. This keeps the banner available even if guest-preload injection is blocked. - Extension store host bar reset: Switching to
axis://newtabnow immediately hides the host install bar (updateExtensionStoreHostBar('')) instead of waiting for the next website navigation. - Downloads popup (in-progress states): Items that are still downloading are now visually dimmed/locked, show
Downloading…metadata with percentage when total size is known, and render an inline progress bar (indeterminate animation if total is unknown). Row open/reveal actions stay disabled until completion, then return to normal styling/behavior. - Downloads popup live tracking source: In-progress state now uses live
will-downloaditems (get-active-downloads) instead of persisted download history, so files no longer appear instantly “done”; metadata can include percentage and ETA (min/sec left) when speed/total are available. - Downloads popup live updates while open: Progress rows now refresh in place without closing/reopening the popup (updates are driven by
axis-download-activityevents + a light polling fallback while the popup is visible). - Downloads popup no-twitch + dedupe: Live updates now patch existing rows in place (no full list teardown/rebuild), eliminating visible twitch; active-vs-folder entries are deduped by normalized filename (
.crdownload/.part/.downloadstripped) so the same file no longer appears as both greyed and normal. - Pinned / unpinned separator: The
#tabs-separatorline uses a smaller horizontal inset (--tabs-separator-inset-inline) so it runs closer to the left and right edges of the tab column. - Mini player after PiP: The dock now appears whenever native PiP closes and the page still has that
<video>(not only when playback stayed unpaused). The dock no longer vanishes just because the guest is paused — it hides on ended, missing media, tab close, or Pause and hide. - Mini player + resize seam:
#sidebar-resize-handleextends to the bottom of the sidebar (wascalc(100% - 40px), which stopped above the footer and looked like a broken edge). Footer inset matches the tab column (14px),z-indexstacks the dock above the seam. - Mini player layout (reference): Expand strip (
max-height: 36px) shows only volume / PiP / dismiss at 26×26 (same as the main transport row); expand inner padding matches the main row (6px) so the two control rows line up. No button background highlights; expand inner is transparent + hairline divider. Title: long names use a mask fade into controls (not…); tooltip = full title + Go to tab. - Mini player hover / title: Lift, shadow, and expand strip follow pointer hover or keyboard
:focus-visibleonly (not plain:focus-within), so a mouse click no longer leaves the dock “stuck” expanded. Long titles slide toward the end on hover with a light, wide edge feather; YouTube tint is a low-contrast left wash (not a strong red band).prefers-reduced-motion: title slide disabled; expand stays open for reachability. Title (“Go to tab”) usespreventDefault/stopPropagationso it reliably switches tabs instead of behaving like dismiss. - Mini player expand controls: Mute and PiP sit on the left; Pause and hide (×) stays on the right. PiP from the dock skips auto-PiP layout/visibility filters (they failed for background tabs) and calls
executeJavaScript(..., true)sorequestPictureInPictureruns with user gesture; exits an existing PiP element when it is not the target video.
Tabs / webviews
- Tab & tab group context menus: Native sidebar menus include Reset Icon after Change Icon — enabled when the tab or tab group has a custom icon (
hasCustomIcon); clears stored icon data and restores default favicon / group glyph (tab-context-menu-action/tab-group-context-menu-actionreset-icon). Tab reset persists pinned state and syncs favorite tiles for favorite runtime tabs. Tab group Change Icon now updates the row:getOrCreateGroupElementreused DOM without refreshing the header —syncTabGroupElementHeaderapplies color / icon / name;findTabGroupKeynormalizes ids for the emoji picker andapplySelectedIcon. - URL bar tint accuracy:
extractUrlBarThemescores multiple candidates (top-of-page vote, header/nav,theme-colormeta, body surface) with agreement boosts and meta penalty when it disagrees with visible chrome; filters cookie strips, buttons/links, and saturated accent chips; walks effective background up the tree; re-runs once after ~480ms on the active tab for late SPA chrome. Still gated on active webview + sequence so tab switches don’t apply stale tints. - Inspect Element: Native (and fallback #webpage-inspect) Inspect Element now calls
webview.inspectElement(x, y)with the right-click coordinates from the guest context-menu payload instead of onlyopenDevTools(), so the Elements panel targets the node under the cursor (e.g. a button). - Trackpad back / forward (guest): Horizontal wheel accumulation threshold (
THRESH_AXIAL~175) and stricter horizontal vs vertical ratio (HORIZONTAL_DOMINANCE) plus a higher minimum |dx| per event inwebview-preload-nav.js/webview-preload-bundle.jsso side-to-side page scrolling is less likely to trigger back/forward than before. - Pinned tabs persistence: Saving pinned tabs no longer depends on being physically above the separator during DOM transitions; persistence now uses normalized tab IDs and each tab’s actual
pinnedstate, preventing intermittent lost pinned tabs after drag/reorder/close flows. - Picture-in-picture (auto): Tab-switch PiP only considers large, visible
<video>elements (minimum layout 112×112 px / 12k px² area, intrinsic frame ≥96px short edge when known,checkVisibility/ opacity) so tiny animated/decorative clips no longer trigger PiP. - Picture-in-picture: Native PiP’s ✕ only ends PiP and shows the sidebar mini player when the
<video>is still there — the shell cannot tell that from “Back to tab,” so the poll never switches tabs on teardown. The URL bar no longer shows a duplicate Back to tab (#url-bar-pip-back-tabremoved); use Go to tab on the sidebar dock or native PiP. Dock title still Go to tab without pausing; Pause and hide pauses and clears the dock. Polling 150 ms.hidePIPclears PiP state. - Cmd+F popup controls: Find buttons (
.search-btn) no longer inherit site-tinted--text-color/--text-color-secondary; they now use fixed dark-mode values with the existing light-mode overrides, so controls follow UI light/dark mode only. - YouTube / SPA reload on tab focus: Switching back to a tab no longer reassigns
webview.srcwhen the guest already has a http(s) page loaded — only loads fromtab.urlwhen the webview is blank.tab.urlsyncs fromgetURL()when it lagged (in-page query/hash changes), so sites like YouTube no longer full-reload and lose playback when refocusing the tab. - URL bar transitions: Themed
.webview-url-baragain cross-fades tint when a page’s colors are applied (e.g. while loading, before/afterextractUrlBarTheme). Tab switches seturl-bar--instant-themefor that hand-off only so the bar does not fade between tabs; the class is cleared after sync styling or the first extract for the new tab. - Trackpad back / forward: Edge hint only: a simple black circular edge pill + white arrow on the gesture side (Safari-ish); the page does not translate. Horizontal gesture direction: swipe right / positive dx → forward, left / negative dx → back (guest preload + macOS
BrowserWindowswipe). Busy gestures queue one follow-up swipe. After the timing windowaxisNavigateAfterGesturepreferswebview.goBack()/goForward()when Chromium reports a stack. Guest wheel thresholds slightly relaxed (webview-preload-bundle.jsinlined).app-commandbrowser-backward/browser-forwardunchanged (semantic keys). - Window resize performance: Resize no longer triggers guest layout pings or
executeJavaScriptresizeon everyResizeObserver/ resize frame — only once after resizing settles (~120ms idle); Chromium already resizesautosizeguests during the drag. Droppedwill-changeon tab<webview>shells and removedaxis-shell-resizingshell blur toggling that churned styles each frame. - Guest pages track window size:
#content-area/#single-view/.webview-containernow use a flexmin-height: 0chain so the page area doesn’t get stuck after sidebar or window resize;ResizeObserver+ windowresizenudge Electron’s<webview>layout and fireresizein the active guest so responsive layouts reflow. - Transparent sites cleanup: Disabling Transparent sites now restores inline page backgrounds changed by the glass-mode DOM patch, including older already-patched pages. Normal browsing also gets an opaque page canvas behind active webviews, so sites like
workspace.google.comno longer show Axis transparency when the setting is off. - New tab overlay blocked webviews: Switching away from
axis://newtabdid not hide#new-tab-page(it stacks atz-index: 50), so the overlay kept covering site webviews — URL bar looked correct while the page stayed blank or showed the wrong layer.switchToTabnow always callsupdateNewTabPageVisibility(false)unless the active tab is the new-tab URL;navigateToUrlInCurrentTabalso hides the overlay when leavingaxis://newtab. - Downloads button while downloading: The URL bar control swaps the arrow icon for an SVG ring — determinate fill from aggregate
receivedBytes/totalBytesacross active items (throttled IPC), or a smooth indeterminate arc when totals are unknown. Tooltip shows Downloading…;aria-busyis set for accessibility. - Web panel shadow:
#content-area::afterstill uses--axis-webview-elevation-shadow(hairline + inset rim + symmetric0 0blurs — no downward-only fake light). Stack retuned so near-edge opacity carries most of the weight and falloff tops out ~44px instead of a 148px haze — reads thicker, tighter, and more like a resting card than a diffuse glow. - Close tab left the page on screen:
_applyFocusAfterTabClosenulledcurrentTabbeforeswitchToTab; if the target id didn’t match theMap(string vs number) or focus failed,switchToTabreturned without selecting a tab while guests were already removed — sidebar updated, stale webview stayed visible. Focus hand-off now normalizes ids with_normalizeTabMapKey, tries neighbors + unpinned + pinned + DOM order, purges#webviews-containerguests with no tab row (_purgeStaleWebviewsInContainer), andswitchToTabrecovers when the id is stale but other tabs exist. Closing a background tab re-runs_prepareWebviewsForTabSwitchfor the active tab. - Idle webview area painted a white frosted pane:
#webviews-containerunconditionally painted--axis-webview-idle-well(linear-gradient +backdrop-filter: blur(44px) saturate(185%)+ inset shadows), andwebview::beforerendered anrgba(255, 255, 255, 0.25)glass overlay; both showed as a white pane wherever the guest stack wasn’t covering pixels. Container backgrounds (base,body.chrome-no-tabs,body.transparent-sites-mode,body.incognito-window) are nowtransparentwith no backdrop / shadow,webview::beforeis removed entirely, and the no-tabs placeholder webviews drop fromopacity: 0.3toopacity: 0; visibility: hiddenso nothing paints where the webview is when no page is active. - Squared webview / shadow corners when idle:
2pxright-edge radii read almost square on Retina,.empty-stateand#webviews-containeronly rounded the bottom-right while the URL bar was hidden (full-height panel), and a duplicate.webview-containerrule forcedborder-radius: 6pxon all corners (fighting the sharp-left seam). Introduced--axis-content-panel-radius(6px) for#content-area::after(lift shadow),.webview-container,.webview-url-bar,#webview/#webviews-container/.empty-state, tab<webview>guests,#webviewwhen the bar is hidden, and the AI chat panel; removed the conflicting6pxall-corners override from the loading block. - Content panel had squared left corners: The stack intentionally used
0radius on the sidebar seam (only the window side was rounded).#content-area::after,.webview-container,.empty-state,.ai-chat-panel, and full-height#webviews-container/#webview/ guests now useborder-radius: var(--axis-content-panel-radius)on all corners;.webview-url-barrounds top-left and top-right; the guest stack below the bar keeps bottom-left + bottom-right only so the bar’s bottom edge stays straight.
Full Changelog: v0.2.7...v0.2.8