Current Version: 0.55
Phraims is a web browser that divides each window into multiple resizable web page frames.
Phraims is a Qt6 app based on QtWebEngine which uses Chromium WebKit.
The eventual goal is to have fully dynamically dockable frames with a stretch goal of having most expected first-class browser features... and yes, including support for traditional tabs.
- arm64 (M1 and above CPUs): Supported
- x64 (Intel CPUs): Work In Progress
- x64: Not Yet Supported - Work In Progress
- arm64: Not Yet Supported - Work In Progress
- x64: Not Yet Supported - Not Yet A Work In Progress
- arm64: Not Yet Supported - Not Yet A Work In Progress
Proper Release Management is still a Work In Progress.
The eventual goal is to be a fully signed release with first class auto-update.
Until then, installs are currently available via:
- Navigate to the Releases page
- Download the appropriate installer for your platform:
- macOS:
Phraims-v{version}-macOS-{arch}.dmg(arm64 for M1+ or x86_64 for Intel) - Windows:
Phraims-v{version}-Windows-{arch}.exe(x64 for Intel/AMD)
- macOS:
- Run the installer; you will need to allow your system security to install:
- macOS: System Settings → Privacy & Security → Open Anyway
- Navigate to the Actions tab
- Click on the latest successful workflow run for your platform
- Scroll down to the "Artifacts" section
- Download the appropriate
Phraims-{Platform}-{arch}artifact (90-day retention) - Extract and run the app; you will need to allow your system security to install
The workflow can also be triggered manually via the "Run workflow" button for building release candidates.
- macOS:
- Settings:
~/Library/Application Support/LookAtWhatAiCanDo/Phraims/settings.ini - Profile:
~/Library/Application Support/LookAtWhatAiCanDo/Phraims/profiles/
- Settings:
- Linux:
- Settings:
~/.config/LookAtWhatAiCanDo/Phraims/settings.ini - Profile:
~/.config/LookAtWhatAiCanDo/Phraims/profiles/
- Settings:
- Windows:
- Settings:
%APPDATA%/LookAtWhatAiCanDo/Phraims/settings.ini - Profile:
%APPDATA%/LookAtWhatAiCanDo/Phraims/profiles/
- Settings:
- New Frame: Click the
+button on any frame or press⌘T(Command-T on macOS) orCtrl+T(other platforms) to add a new frame after the currently focused frame.
When adding a new frame the address bar is automatically focused so you can start typing immediately. - Open Link in New Frame: Hold
⌘(Command on macOS) orCtrl(other platforms) and click a link to open it in a new frame adjacent to the current one. Alternatively, right-click a link and select "Open Link in New Frame" from the context menu. - Remove Frame: Click the
-button on any frame to remove it (confirmation required) - Reorder Frames: Use the
↑and↓buttons to move frames up or down - Double-click any splitter handle to instantly resize the two adjacent panes to equal sizes (50/50 split).
- Reload Frame: Press
⌘R(macOS) orCtrl+R(other platforms) to reload the focused frame, or useView -> Reload Frame. - Reload Frame (Bypass Cache): Press
⌘⇧R(macOS) orCtrl+Shift+R(other platforms) to force-refresh the focused frame viaView -> Reload Frame (Bypass Cache).
Media Playback Preservation
When working with frames that have active media playback (audio/video) or stateful content:
- ✅ Adding frames (in Vertical/Horizontal modes): Media playback continues in existing frames
- ✅ Removing frames: Media playback continues in remaining frames
- ❌ Reordering frames (up/down buttons): Media playback stops and pages reload in all frames
- ❌ Adding frames in Grid mode: Media playback stops and pages reload in all frames
Technical Details
The application uses a "surgical" approach for frame addition and removal in Vertical and Horizontal layout modes, which preserves the state of existing frames including media playback, scroll position, and form data. However, frame reordering requires rebuilding the entire layout due to Qt's QSplitter widget limitations - it doesn't support changing widget order without removing and re-adding them.
Grid mode frame addition also requires layout rebuilding due to the complexity of nested splitters.
Future Improvements
These limitations may be improved in future versions through alternative approaches such as drag-and-drop frame reordering, though such features would still face the same underlying Qt layout constraints.
- New Window: Press
⌘N(Command-N on macOS) orCtrl+N(other platforms) - New Incognito Window: Press
⇧⌘N(Shift+Command-N on macOS) orShift+Ctrl+N(other platforms) to open a private browsing window - Toggle DevTools: Press
F12to toggle developer tools for the focused frame
- Each section is equally sized using layout stretch factors
- Use the Layout menu to switch between Grid, Vertical, and Horizontal arrangements
Phraims supports multiple browser profiles, each with its own separate browsing data, cookies, cache, and history. This allows you to maintain completely isolated browsing contexts within the same application.
Access profile management through the Profiles menu in the menu bar:
-
New Profile...: Create a new profile with a custom name
- Profile names cannot contain slashes (/ or )
- Each profile gets its own storage directory
-
Rename Profile...: Rename an existing profile
- Select the profile to rename from the list
- Enter a new name (cannot contain slashes)
- If you rename the currently active profile, it will be updated automatically
-
Delete Profile...: Permanently delete a profile and all its data
- Select the profile to delete from the list
- Confirmation is required before deletion
- Cannot delete the last remaining profile
- All cookies, cache, history, and other data will be permanently removed
-
Open Profiles Folder (debug builds only): Opens the profiles directory in your system file browser
- Shows all profile directories on your filesystem
- Useful for backup, inspection, or manual management of profile data
- Only available when running a debug build of the application
-
Profile List: Shows all available profiles
- The currently active profile is marked with a checkmark
- Click any profile to switch to it immediately
- Each window title displays the current profile name (e.g., "Group 1 (3) - Work")
- Each window remembers which profile it was using and restores it on app restart
- When you switch profiles, all frames in the current window are rebuilt with the new profile
- New windows use the most recently selected profile by default
- Profile data is stored in your application data directory under
profiles/<profile-name>/
When you first launch Phraims, a "Default" profile is automatically created and used. You can create additional profiles and switch between them at any time.
Phraims supports Incognito (private) browsing windows for ephemeral sessions that do not persist history, cookies, or other browsing data.
- Keyboard Shortcut: Press
⇧⌘N(Shift+Command-N on macOS) orShift+Ctrl+N(other platforms) - Menu: Select
File -> New Incognito Window
- Isolated Storage: Each Incognito window uses a separate off-the-record profile that does not persist to disk
- No History: Browsing history, cookies, cache, and other data are discarded when the window closes
- Visual Indicator: Incognito windows display "Incognito" in the title bar to distinguish them from normal windows
- No Persistence: Window geometry, frame addresses, and splitter sizes are not saved between sessions
- No Profile Management: The Profiles menu is not available in Incognito windows since they use ephemeral profiles
- Independent Operation: Incognito and normal windows operate completely independently without cross-contamination
- Temporary Browsing: View websites without affecting your browsing history or saved data
- Multiple Logins: Log into the same website with different accounts simultaneously
- Testing: Test website behavior without cached data or cookies
- Privacy: Browse sensitive content without leaving traces on your system
- Use the
A-,A+, and1xbuttons in each frame header (orView -> Increase/Decrease/Reset Frame Scale) to zoom the embedded page without touching splitter sizes or header chrome. These controls are simply a shortcut for adjusting the QWebEngineView zoom on a frame-by-frame basis. - The UI chrome stays at a consistent size so controls remain easy to target even when a page is zoomed way in/out.
- Zoom choices are stored per frame in the current layout. Closing and reopening the app restores the last zoom factor for each saved slot.
This application supports persisting small DOM CSS "patches" you create while using the inspector.
A patch is a site-scoped CSS tweak (for example hiding an element) that the app will automatically re-apply whenever a matching page is loaded or navigated to.
How it works
- Patches are stored in JSON at the application data root (the app prints this path on startup). The file is named
dom-patches.json. - Each patch contains:
urlPrefix— full URL prefix to match (startsWith matching). Use a value likehttps://studio.youtube.com/live_chatto match that page and its navigations.selector— CSS selector for the element to target (e.g.#card).css— CSS declarations applied to the selector (e.g.display: none;).enabled— whether the patch is active.
Using the feature
- Open
Tools -> DOM Patches. - Click Add and fill in the fields. Example to hide the YouTube Studio chat card:
- URL prefix:
https://studio.youtube.com/live_chat - CSS selector:
#card - CSS declarations:
display: none;
- URL prefix:
- Save and reload the page — the element will be hidden automatically. Patches persist across app restarts.
Notes & limitations
- Matching is simple
startsWithon the page URL. If you need broader matching we can add glob/regex options. - This is CSS-only for now (safe and performant). If a rule needs JS, it can be added later.
- For single-page apps the app re-applies patches on URL changes and after page loads; that covers most SPA navigations.
- The manager is a lightweight dialog — future enhancements can include a context-menu helper to capture a selector directly from the page.
Data format example (dom-patches.json entry):
{
"id": "550e8400-e29b-41d4-a716-446655440000",
"urlPrefix": "https://studio.youtube.com/live_chat",
"selector": "#card",
"css": "display: none;",
"enabled": true
}
Removing or editing patches
- Use
Tools -> DOM Patchesto edit or delete saved patches. Deleting a patch removes it from the JSON file and the page will return to its original styling.
Privacy & safety
- Patches run only locally in this application. They are not synced or sent anywhere.
The web view context menu provides quick access to common actions:
- Navigation: Back, Forward, Reload
- Editing: Cut, Copy, Paste, Select All
- Copy Link Address: Copies the fully encoded hyperlink under the cursor to the clipboard (only visible on links)
- Open Link in New Frame: Opens the link in a new frame adjacent to the current one (only visible on links)
- Uses the active profile (or incognito profile) of the current window
- Preserves incognito mode if the window is in incognito mode
- Translate…: Translate selected text or the entire page using Google Translate
- If text is selected, opens translation of the selected text
- If no text is selected, opens full page translation
- Opens in a new Phraims window
- Inspect…: Opens DevTools for page inspection and debugging
Phraims includes built-in update checking to help you stay current with the latest features and fixes.
- Access via Help → Check for Updates... in the menu bar
- Phraims will query GitHub for the latest release
- If an update is available, you'll see:
- Version comparison (current vs. latest)
- Release notes highlighting what's new
- Platform-appropriate update options
macOS
- Integrated with Sparkle framework for seamless updates
- If Sparkle is available:
- Downloads and verifies updates automatically
- Shows native update dialog with release notes
- Installs update and relaunches without leaving the app
- No manual approval needed (quarantine-safe)
- If Sparkle not available (development builds):
- Opens your browser to download the latest
.dmg - Install manually by dragging to Applications folder
- Opens your browser to download the latest
Windows
- Integrated with WinSparkle library for seamless updates
- If WinSparkle is available:
- Downloads and verifies updates automatically (DSA/EdDSA signatures)
- Shows native update dialog with release notes
- Installs update with proper elevation and relaunches
- Automatic rollback if update fails
- Same appcast feed as macOS Sparkle
- If WinSparkle not available (development builds):
- Opens your browser to download the latest installer
- Install manually with elevated privileges
Linux
- Opens your browser to the GitHub releases page
- Download and install manually using your preferred method
- Respects distribution package management conventions
All releases are available at: https://github.com/LookAtWhatAiCanDo/Phraims/releases
Choose the appropriate file for your platform:
- macOS:
Phraims-v{version}-macOS-{arch}.dmg - Windows:
Phraims-v{version}-Windows-{arch}.exe - Linux: See releases page for available packages
Currently only stable releases are published. Beta/nightly channels may be added in the future.
Update checks connect to GitHub's API to fetch release information. No personal data or usage statistics are transmitted. The request includes only the application name and current version in the User-Agent header.
- Improve Menu
- Make similar to Chrome, VSCode, OBS, etc.
- GOOD GRIEF LOTS OF OPTIONS!
Is there a cross-platform design guideline for what an app should have?!?!
- Support multiple instances
Chromium may not support multiple instances of a single profile. - Add pre-set collections that can be quickly recalled
aka: Bookmarks/Groups - Make a serious browser
- Popup Menu
- ...
- Copy Link Address
- ...
Translate✓ (completed)- ...
- Browser History
- Bookmarks
- Passkeys!!
- Downloads Shift-Command-J
- Extensions (GitHub Issue #9)
- Settings
- ...; all expected things from a browser!
- User-Agent
- Popup Menu
- Support opening
phraims://inspectsimilar to chrome://inspect
- Discord page blank white
- messenger.com page not loading
- main.cpp - Application entry point and initialization
- SplitWindow - Main window class with menu bar and splitter management
- SplitFrameWidget - Individual web view frame with navigation controls
- MyWebEngineView (header-only) - Custom QWebEngineView with context menu support
- DomPatch - DOM patching system for CSS customizations
- EscapeFilter (header-only) - Fullscreen escape key handler
- Utils - Shared utilities and helper functions
Simple classes like EscapeFilter and MyWebEngineView use header-only implementations for easier maintenance.
Build (requires CMake + Qt 6 + WebEngine w/ proprietary codecs enabled; the Homebrew qt6 package is confirmed to ship with -DFEATURE_webengine_proprietary_codecs=ON):
# from repository root
brew install qt6
mkdir -p build && cd build
cmake .. -DCMAKE_PREFIX_PATH="$(brew --prefix qt6)"
cmake --build . --config Release
./PhraimsTo enable seamless in-app updates on macOS, install the Sparkle framework before building:
# Download and install Sparkle 2.x
# Visit https://sparkle-project.org/ for latest release
# Extract and copy Sparkle.framework to one of these locations:
# - /Library/Frameworks/
# - ~/Library/Frameworks/
# - or place in project root under Frameworks/
# CMake will automatically detect and link Sparkle if found
cmake .. -DCMAKE_PREFIX_PATH="$(brew --prefix qt6)"
cmake --build . --config ReleaseIf Sparkle is not found, CMake will show a warning and build without Sparkle support. The app will still function normally but will use manual download links instead of in-app updates.
The application uses platform-specific icon formats for proper display in taskbars, title bars, and file explorers:
- macOS: Uses
resources/phraims.icns(generated from PNG sources viaicons.sh) - Windows: Uses
resources/phraims.ico(multi-resolution icon embedded via resource script) - Linux: Currently uses default Qt icon (platform-specific icon support planned)
Regenerating Windows Icons
If the source PNG files in phraims.iconset/ are updated, regenerate the Windows .ico file:
# Requires Python 3 with Pillow
pip3 install Pillow
python3 generate_ico.pyThis script creates a multi-resolution resources/phraims.ico containing standard Windows sizes (16, 32, 48, 64, 128, 256 pixels). The icon is automatically embedded into the Windows executable via the resources/phraims.rc.in resource script during CMake configuration and build.
macOS Icon Regeneration
To regenerate resources/phraims.icns from the source PNG:
./icons.sh # Requires macOS with sips and iconutil- macOS CI builds an arm64 app bundle in
build/macos-arm64/using Homebrew Qt for base modules plus the custom QtWebEngine built in the privateLookAtWhatAiCanDo/QtWebEngineProprietaryCodecsrepo (default prefix.qt/6.9.3-prop-macos-<arch>) and packages it as a DMG (Homebrew Qt bottles are arm64-only on Apple Silicon runners). - The primary workflow (
.github/workflows/build-phraims.yml) first tries to download a cached QtWebEngine prefix artifact produced by the corresponding platform workflow in theQtWebEngineProprietaryCodecsrepository; if absent it fails fast rather than rebuilding inline. macdeployqtreceives both the Homebrew Qt module libpaths and the custom QtWebEngine prefix to avoid rpath resolution errors in plugins before creating the DMG.- When bumping QtWebEngine, run the
Build QtWebEngine macOSworkflow in the private repo to refresh the artifact. - The unified build workflow runs both macOS and Windows builds in separate jobs with architecture matrices.
macOS builds run on
macos-26(arm64) andmacos-15-intel(x86_64), producing per-arch QtWebEngine artifacts (qtwebengine-macos-<ver>-<arch>) and DMGs (Phraims-macOS-<arch>). - Each prefix is stored under
.qt/<ver>-prop-<os>-<arch>. The workflow downloadsqtwebengine-<os>-<ver>-<arch>from the privateLookAtWhatAiCanDo/QtWebEngineProprietaryCodecsrepo usingPRIVATE_QTWEBENGINE_TOKENbefore building, then packages tobuild/<os>-<arch>/Phraims.{dmg,exe}. - If the workflow cannot download the QtWebEngine artifact (e.g., token expired),
refresh @paulpv’s PAT named
LAWACD QtWebEngineProprietaryCodecs, then paste the new token into the Phraims repository secretPRIVATE_QTWEBENGINE_TOKENand rerun. The current 366-day PAT was created 2025/11/26 and expires 2026/11/27.
Windows QtWebEngine builds are produced by the private Build QtWebEngine Windows workflow; it emits per-arch prefixes
(qtwebengine-windows-<ver>-x64 / qtwebengine-windows-<ver>-arm64) that contain the built QtWebEngine runtime and resources.
The unified build workflow downloads the appropriate QtWebEngine artifact via ci/get-qtwebengine-windows.ps1,
ensures a host Qt kit (via qmake on PATH or by installing one with aqtinstall), builds Phraims,
runs windeployqt to assemble a deploy folder, and then applies a packaging-time replacement step:
the proprietary QtWebEngine runtime files from the downloaded prefix are copied into the deploy folder (overwriting
matching files) so the packaged application ships the proprietary WebEngine payload without modifying the host Qt kit.
This approach avoids overwriting system Qt installs and reduces permission/ABI risks.
Windows builds run on windows-2025 (x64) with support for windows-11-arm (arm64).
Packaging uses the custom QtWebEngine produced by the QtWebEngineProprietaryCodecs repository
(default install prefix .qt/6.9.3-prop-macos; override with QT_WEBENGINE_PROP_PREFIX to point at the copied prefix).
Download/copy the prefix from that repo first so the macOS bundle can be assembled locally.
Run ./ci/build-phraims-macos.sh to:
- update Homebrew
- install dependencies
- build Release with Ninja in
build/macos-<arch>(defaultbuild/macos-arm64; override withBUILD_ARCH/MACOS_ARCH) - run
macdeployqt - sync QtWebEngine resources
- fix rpaths
- sign
- emit
build/macos-<arch>/Phraims.dmg.
It also validates that every dependency resolves inside the bundle and checks WebEngine resources.
Set FORCE_BREW_UPDATE=0 to skip brew update if needed.
- Normal run:
./ci/build-phraims-macos.sh - Debug info:
DEBUG=1 ./ci/build-phraims-macos.sh(shows macdeployqt log, staging/Frameworks listings, and rpaths for the main binary and QtWebEngineProcess)
The project uses GitHub Actions to automatically build macOS and Windows binaries on every push to main and on pull requests.
The unified workflow (.github/workflows/build-phraims.yml) builds both platforms in a single run with separate jobs for each OS.
This ensures the codebase stays healthy and provides downloadable artifacts for testers.
open build/Qt_6_10_0_for_macOS-Debug/Phraims.app --args --webEngineArgs --remote-debugging-port=9222
Open Chrome to chrome://inspect
These tests define the desired behavior for layout selection and splitter persistence.
-
Setting a layout
- Action: Select a layout from the
Layoutmenu (Grid, Stack Vertically, Stack Horizontally). - Expected: The UI rebuilds so all frames are visible and equally sized.
- Action: Select a layout from the
-
Manually adjusting splitters
- Action: Drag a splitter handle to change sizes of adjacent frames.
- Expected: The UI updates immediately to reflect the new sizes. The new sizes are stored on application exit and will be loaded on next launch.
-
Close app and reopen — manual splitter positions persist
- Action: With manual splitter adjustments made, close the application and then relaunch it.
- Expected: The frames open with the splitters in the last positions the user set before exit.
-
Re-setting the layout (re-selecting the currently selected layout)
- Action: Choose the currently-active layout again from the
Layoutmenu. - Expected: The layout fully resets and rebuilds; all frames are laid out evenly (default sizes). Persisted splitter sizes are NOT applied when re-selecting a layout.
- Action: Choose the currently-active layout again from the
-
Changing to another layout
- Action: Select a different layout from the
Layoutmenu. - Expected: The layout switches and rebuilds. The new layout starts in default (evenly distributed) sizes. Persisted splitter sizes are only applied on application startup — not when changing layouts during a running session.
- Action: Select a different layout from the
-
Per-frame zoom persists
- Action: Use the
A-/A+buttons (or the View menu actions) to change the zoom of a frame, quit the application, and relaunch it. - Expected: Each frame reopens with the exact zoom factor that was active before quitting. Only the web content should change size; splitter handles and chrome stay put.
- Action: Use the
-
Creating a new profile
- Action: Select
Profiles -> New Profile...from the menu bar, enter a name (e.g., "Work"), and click OK. - Expected: The profile is created successfully. A confirmation message appears. The Profiles menu now shows the new profile in the list.
- Action: Select
-
Switching between profiles
- Action: Open the Profiles menu and select a different profile from the list (e.g., switch from "Default" to "Work").
- Expected: The window immediately switches to the selected profile. All frames are rebuilt with the new profile's data. The checkmark in the Profiles menu moves to the newly selected profile. Cookies and browsing data are now isolated to the selected profile.
-
Profile persistence across restarts
- Action: Switch to a specific profile (e.g., "Work"), load a website that sets a cookie, close the app, and reopen it.
- Expected: The window reopens using the last-selected profile ("Work"). The website's cookie is still present, confirming data persistence within that profile.
-
Renaming a profile
- Action: Select
Profiles -> Rename Profile..., choose a profile from the list, enter a new name, and confirm. - Expected: The profile is renamed successfully. If the renamed profile was active, the current window continues using it with the new name. The Profiles menu reflects the new name.
- Action: Select
-
Deleting a profile
- Action: Select
Profiles -> Delete Profile..., choose a profile (not the current one), and confirm deletion. - Expected: A confirmation dialog appears warning about permanent data loss. After confirming, the profile and all its data are deleted. The profile disappears from the Profiles menu. If multiple profiles exist, deletion succeeds; if only one profile remains, deletion is blocked with an error message.
- Action: Select
-
Profile data isolation
- Action: Create two profiles (e.g., "Personal" and "Work"). In "Personal", log into a website. Switch to "Work" and visit the same website.
- Expected: The website in the "Work" profile does not show the logged-in state from "Personal", confirming complete data isolation between profiles.
-
Opening an Incognito window
- Action: Press
Shift+Command+N(macOS) orShift+Ctrl+N(other platforms), or selectFile -> New Incognito Window. - Expected: A new window opens with "Incognito" in the title bar. The window starts with a single empty frame.
- Action: Press
-
Incognito window isolation
- Action: Open an Incognito window, visit a website that sets a cookie or requires login, then close the Incognito window. Reopen a new Incognito window and visit the same website.
- Expected: The second Incognito window does not have the cookie or login state from the first session, confirming ephemeral storage.
-
Incognito window non-persistence
- Action: Open an Incognito window, load several websites in multiple frames, adjust splitter sizes, then close the application. Relaunch the application.
- Expected: The Incognito window does not reopen. Only normal (non-Incognito) windows are restored with their saved state.
-
Normal and Incognito window independence
- Action: Open a normal window and an Incognito window side-by-side. Log into a website in the normal window. Visit the same website in the Incognito window.
- Expected: The Incognito window does not share the login state from the normal window, confirming complete isolation between normal and Incognito sessions.
Notes
- Persisted splitter positions are only loaded once at application startup. During normal runtime, selecting or re-selecting layouts resets to default split positions.
- The app persists splitter sizes on exit so they can be used for the next application launch.
- Recommended: test quickly by resizing splitters, closing the app, and reopening to verify persistence.
- Profile data is stored per-profile in separate directories under the application data location (shown at startup).
Phraims follows semantic versioning. The current version is defined in CMakeLists.txt and exposed throughout the application:
- CMake: Set via
project(Phraims VERSION x.y.z) - About Dialog: Access via
Help -> About Phraimsmenu item to see version and project link - Build Logs: Version is logged at application startup
- Build Artifacts: Version appears in macOS bundle Info.plist and packaging metadata
To update the version:
- Edit the
VERSIONin theproject()command inCMakeLists.txt - CMake will automatically regenerate
version.hwith the new version - Rebuild the application
- Update the version number at the top of this README