Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
d1b25f4
feat: log viewer, downloads, settings and document viewer as separate…
rodrigok Aug 13, 2026
69a476f
chore: bump version to 4.17.0-alpha.1 (#3456)
jeanfbrito Aug 13, 2026
78b57a0
docs: correct update-channel metadata claims and note admin merges in…
jeanfbrito Aug 18, 2026
150b36c
tests: raise full-surface line coverage to 71.19% with quick-win spec…
jeanfbrito Aug 18, 2026
868d12f
feat: SAML auth through deeplink (#3458)
yash-rajpal Aug 24, 2026
c5bcaf0
feat(tray): show workspace presence status in the tray icon and menu …
jeanfbrito Aug 26, 2026
2732989
fix: dedupe Windows notification quick replies (#3467) (#3468)
Moku151 Aug 26, 2026
45c9c9b
fix: Windows notification quick replies lost after toast dismissal (#…
jeanfbrito Aug 26, 2026
df12e07
fix(tray): hide presence status when the active server failed to load…
jeanfbrito Aug 26, 2026
c691a89
chore: bump version to 4.17.0-alpha.2 (#3473)
jeanfbrito Aug 26, 2026
e8abf7f
fix(updates): make Check for updates work on store builds (Mac App St…
jeanfbrito Aug 28, 2026
8c6bd9e
fix: keep macOS native fullscreen when pressing ESC (#3433)
scuciatto Aug 28, 2026
9bd5999
feat(logViewer): correctness, performance, and UX improvements (#3446)
jeanfbrito Aug 28, 2026
437f0f3
fix(updates): install macOS updates through electron-updater (#955) (…
jeanfbrito Aug 28, 2026
5b07559
chore: bump version to 4.17.0-alpha.3 (#3474)
jeanfbrito Aug 28, 2026
5f77aef
Revert "fix: keep macOS native fullscreen when pressing ESC (#3433)" …
jeanfbrito Aug 31, 2026
729869a
chore: bump version to 4.17.0 (#3482)
jeanfbrito Aug 31, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
14 changes: 11 additions & 3 deletions .claude/skills/ship-release/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,15 +106,19 @@ push, release publish) is gated on explicit user approval.
4. Commit `chore: bump version to <version>`, push, open a PR to **`dev`**.
5. Wait for `validate-pr` checks. **GATE: show PR URL + checks status. STOP
until the user says merge.**
6. Squash-merge: `gh pr merge <PR> --squash`.
6. Squash-merge: `gh pr merge <PR> --squash`. Branch protection requires 1
approving review, so this typically needs `--admin` (the release manager
has bypass) — otherwise `gh pr merge` refuses with "requirements have not
been met".

### Stable (promotion)

1. Same worktree setup as alpha, off fresh `origin/dev`.
2. Bump `"version"` in `package.json` to the bare version (drop the
pre-release suffix, e.g. `4.17.0-alpha.6` → `4.17.0`).
3. **GATE**, commit, push, open a bump PR to **`dev`**. Wait for checks.
**GATE: STOP until the user says merge.** Squash-merge.
**GATE: STOP until the user says merge.** Squash-merge (branch protection
requires 1 approving review, so this typically needs `--admin`).
4. `git -C "$RELEASE_WT" fetch origin dev` and confirm the merge commit is
HEAD of `origin/dev` with `package.json` at TARGET.
5. Open the **release PR**: `dev` → `master`
Expand Down Expand Up @@ -142,7 +146,8 @@ push, release publish) is gated on explicit user approval.
pushing.
3. Bump `"version"` in `package.json` to `X.Y.Z`, commit, push a bump PR
targeting **`release/<X.Y.x>`**. Wait for checks. **GATE: STOP until the
user says merge.** Squash-merge.
user says merge.** Squash-merge (branch protection requires 1 approving
review, so this typically needs `--admin`).

## Phase 3 — Merge & tag

Expand All @@ -169,6 +174,9 @@ or stay `cd`'d in) — never in the user's own checkout.
```sh
gh pr merge <RELEASE_PR> --merge
```
Branch protection requires 1 approving review, so this typically needs
`--admin` (the release manager has bypass) — otherwise `gh pr merge`
refuses with "requirements have not been met".
2. `git -C "$RELEASE_WT" fetch origin master` and confirm the merge commit
is HEAD of `origin/master` and its `package.json` has TARGET.
3. **GATE: confirm with the user before pushing the tag.**
Expand Down
12 changes: 12 additions & 0 deletions .github/workflows/pull-request-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -636,6 +636,16 @@ jobs:
}
}
core.setOutput('artifact_urls', artifactUrls.trim());
const now = new Date();
const builtAtLocal = new Intl.DateTimeFormat('en-GB', {
timeZone: 'America/Sao_Paulo',
weekday: 'short', day: '2-digit', month: 'short', year: 'numeric',
hour: '2-digit', minute: '2-digit', hour12: false,
}).format(now) + ' (UTC-3)';
const builtAtUtc = now.toISOString().replace('T', ' ').slice(0, 16) + ' UTC';
const shortSha = context.payload.pull_request.head.sha.slice(0, 7);
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}`;
core.setOutput('build_info', `_Built from \`${shortSha}\` on ${builtAtLocal} · ${builtAtUtc} · [workflow run](${runUrl})_`);

- name: Post PR Comment with the Artifact links
if: steps.get-artifact-urls.outputs.artifact_urls != ''
Expand All @@ -645,4 +655,6 @@ jobs:
header: ${{ runner.os }}-installer
message: |
### ${{ runner.os }} installer download
${{ steps.get-artifact-urls.outputs.build_info }}

${{ steps.get-artifact-urls.outputs.artifact_urls }}
65 changes: 63 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ yarn workspaces:build # Build all workspaces
- After building `desktop-release-action`, remove the nested dist:
`rm -rf workspaces/desktop-release-action/dist/dist` — the action only
needs `workspaces/desktop-release-action/dist/index.js`.
- `app/` mirrors `src/public` (the `syncPublicAssets()` rollup plugin):
files removed from `src/public` are purged from `app/` on build, and
`yarn start`'s watcher rebuilds/relaunches when `src/public` assets change.

## Branching Model

Expand Down Expand Up @@ -72,6 +75,10 @@ yarn workspaces:build # Build all workspaces
`26080`, the second same-month build is `26081`. Check the current value
and the date of its last bump (`git log -p --follow -- electron-builder.json`)
before incrementing — do not guess an arbitrary increment.
- `yarn build-assets` re-encodes every PNG/ICO it touches, including ones
whose source did not change; commit only the assets whose SVG/component
changed and `git checkout --` the rest (byte noise otherwise floods the
diff).

## Releases And Tagging

Expand Down Expand Up @@ -134,6 +141,20 @@ yarn workspaces:build # Build all workspaces
color/animation tokens, read `docs/desktop-ui-guidelines.md` — token
semantics and traps, Fuselage geometry/timing facts, the button-dimming and
SVG transform-origin pitfalls, and layout rules learned in PRs #3441/#3443.
- Tray icons are status-only on macOS, Windows and Linux: six states per
platform — `default`, `presence-{online,away,busy,offline}`, `disconnected`
(`src/ui/main/icons.ts`). The unread count is never baked into the tray
image; it lives on the Windows taskbar overlay (`rootWindow.ts`
`setOverlayIcon`), the macOS menu-bar title and the Linux tray tooltip.
- Presence bullets reuse Fuselage `StatusBullet` glyphs
(`src/ui/icons/PresenceBullet.tsx`: filled / clock cut-out / bar cut-out /
hollow ring; `DisconnectedBadge.tsx`: filled amber with a bold `!`). Any
overlay with transparent cut-outs or a hollow shape MUST pass `AppIcon`'s
`cutout` prop (`PresenceBulletCutout`), otherwise the rocket shows through
the holes.
- Tray-menu bullet icons are 12pt assets under `src/public/images/presence/`
(regenerate with `yarn build-assets --presence-menu-icons`), matching
Fuselage's 12px bullet next to 14px text.

## Testing

Expand All @@ -152,8 +173,12 @@ yarn workspaces:build # Build all workspaces
paint (a clipped SVG passes every DOM assertion). Use the `dev-app-verify`
skill (`skills/dev-app-verify/SKILL.md`) to drive and screenshot the running
`yarn start` app via the port-9339 inspector, and the Developer Mode menu
items (`Simulate Update Flow` / `Simulate Download`) to exercise the flows
without real downloads/updates.
items (`Simulate Update Flow` / `Simulate Download` / `Simulate
Disconnected`) to exercise the flows without real downloads/updates.
`Simulate Disconnected` is a Developer menu checkbox that forces the active
workspace's presence connection to `disconnected` read-side only — the real
connection stays up — so the disconnected tray icon and menu line can be
checked without dropping the network.
- Screen-capture / WebRTC / portal behavior CANNOT be validated in
software-rendered VMs — Chromium gates the PipeWire capture path on
hardware GL (the gate moves between Electron versions). Validate on
Expand All @@ -173,6 +198,42 @@ const runtimeDir = process.env.XDG_RUNTIME_DIR || `/run/user/${process.getuid?.(
this: `process.getuid()`, `process.getgid()`, `process.geteuid()`,
`process.getegid()`.

## Windows Notifications

- A Windows Action Center card stays repliable indefinitely, and Electron emits
the JS `close` event when the banner times out
(`NotificationDismissed(should_destroy=false)`). Calling `close()` on the
instance does NOT remove the card. Do NOT tie per-notification state — reply
routing, target webContents, preload event handlers — to a `close` or dismiss
signal, or replies typed later are dropped. Keep it (bounded) until the app
decides the notification is finished.
- The web client auto-closes every desktop notification 10s after showing it
(`useNotification.ts`; the server never sends `duration`, so the fallback is
always used). Expect that close to arrive while the card is still on screen.
- Only reply and action-button interactions carry activation arguments
(`type=...&tag=<id>`, read via `Notification.handleActivation`, win32).
A click on the toast body carries none and arrives ONLY as the instance
`click` event — keep that listener unconditional.
- `Notification.handleActivation` REPLACES the stored callback rather than
adding one. A debug probe that registers its own handler silently
unregisters the app's — do not use it to observe production behavior.
- Enter does NOT submit a toast reply; only the toast's Reply button does, and
the card closes on submit whether or not the app received anything. Assert
replies by querying the server for the message, never by watching the UI.
- Log dropped activations through `loggers.notifications`
(`src/logging/scopes.ts`). `console.warn` from the main process is invisible
in packaged builds, which makes a dropped reply untraceable in the field.
- Full investigation history:
`docs/postmortem-notification-quick-reply-sup-1097.md`.

## Startup Debugging

- `yarn start` relaunches Electron once per rollup bundle for the first
~60 s (each `writeBundle` restarts the app), so the tray/menu-bar icon
flickers or is absent until `waiting for changes` prints. Judge tray state
only after that, and `pkill` orphaned worktree Electrons first — a
leftover instance keeps port 9339 and shows a second menu-bar icon.

## QA Flow Authoring

When creating or updating QA assets under `qa/`, read these first:
Expand Down
23 changes: 12 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -231,17 +231,18 @@ a single server mode.

#### The settings that can be overridden are:

| Setting | Description |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------ |
| `"isReportEnabled": true` | Sets if the bugs will be reported to developers. |
| `"isInternalVideoChatWindowEnabled": true` | Sets if video calls will be opened in an internal window. |
| `"isFlashFrameEnabled": true` | Sets if the flash frame will be enabled. |
| `"isMinimizeOnCloseEnabled": false` | Sets if the app will be minimized on close. |
| `"doCheckForUpdatesOnStartup": true` | Sets if the app will check for updates on startup. |
| `"isMenuBarEnabled": false` | Windows/Linux: `true` keeps the menu bar always visible; `false` auto-hides it (Alt shows it temporarily). Unused on macOS. |
| `"isTrayIconEnabled": true` | Enables Tray Icon, the app will be hidden to the tray on close. Overrides `"isMinimizeOnCloseEnabled"` |
| `"isUpdatingEnabled": true` | Sets if the app can be updated by the user. |
| `"isAddNewServersEnabled": true` | Sets if the user can add new servers. |
| Setting | Description |
| ------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `"isReportEnabled": true` | Sets if the bugs will be reported to developers. |
| `"isInternalVideoChatWindowEnabled": true` | Sets if video calls will be opened in an internal window. |
| `"isFlashFrameEnabled": true` | Sets if the flash frame will be enabled. |
| `"isMinimizeOnCloseEnabled": false` | Sets if the app will be minimized on close. |
| `"doCheckForUpdatesOnStartup": true` | Sets if the app will check for updates on startup. |
| `"isMenuBarEnabled": false` | Windows/Linux: `true` keeps the menu bar always visible; `false` auto-hides it (Alt shows it temporarily). Unused on macOS. |
| `"isTrayIconEnabled": true` | Enables Tray Icon, the app will be hidden to the tray on close. Overrides `"isMinimizeOnCloseEnabled"` |
| `"isUpdatingEnabled": true` | Sets if the app can be updated by the user. |
| `"isAddNewServersEnabled": true` | Sets if the user can add new servers. |
| `"isNotificationQuickReplyEnabled": true` | Sets if notifications are created with an inline reply field. When set to `false`, notifications are created without the inline reply field (Windows/macOS). |

##### Single server mode

Expand Down
8 changes: 5 additions & 3 deletions docs/COVERAGE.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@ Running log of test-coverage milestones for Rocket.Chat.Electron. Append a new r
| 2026-06-22 | Phase 2 — extract-and-test moderate modules (#3364) | 26.34% | 26.76% | 23.61% | 19.17% | 802 | navigation cert utils, ScreenSharingRequestTracker, browserLauncher, ipc/renderer, logging factory; relocated a silently-never-running getOutlookEvents spec (0%→~95%). |
| 2026-06-22 | Phase 3 — React Testing Library + renderer component tests (#3365) | 33.29% | 33.65% | 29.33% | 27.31% | 912 | Added RTL infra (`src/ui/test-utils.tsx`) + 14 component specs (dialogs, containers, leaf, ui/utils). 3 specs held back (see Known gaps). |
| 2026-06-22 | RC-style Codecov reporting (#3366) | 33.29% | 33.65% | 29.33% | 27.31% | 912 | No coverage change — switched CI to Codecov (informational, no hard gate) to match the main monorepo. |
| 2026-08-02 | Pre-wave snapshot (local, full `src/**` collect) | 46.72% | 46.94% | 44.28% | 41.36% | ~1600 | Intermediate baseline before the quick-win wave; includes post–Phase-3 growth already on master. |
| 2026-08-02 | Quick-win wave (`chore/test-coverage-quick-wins`) | 70.14% | 70.35% | 60.54% | 67.63% | 1842 | Full `src/**` collectCoverageFrom (no denominator gaming). Orphan specs nested for discovery; settings/UI/dialog RTL; main IPC (video call, Outlook, log viewer, notifications, downloads); preload coverage via main/node project under `--coverage`; ErrorView render short-circuit fix; pure helpers extracted (`validateVideoCallUrl`, `logFormatters`). |

## Known gaps / next steps

- **Goal:** 50% lines. At 33.29% as of the last row — reachable via the renderer (`ui/`) layer alone; no need to test webview/Electron-integration files.
- **Quarantined specs** (written but held back — they leak async/DOM teardown that the strict `uncaughtException` handler in `src/.jest/setup.ts` turns into a suite-killing `process.exit(1)`): `ServersView/ErrorView`, `ServerInfoContent`, `AboutDialog`. Re-enabling these (with proper fake-timer / async cleanup) is the cheapest next win. See `docs/KNOWN_ISSUES.md`.
- **Remaining headroom:** the rest of `ui/components` dialogs/containers, and non-webview `ui/main` logic.
- **Goal met:** 70% lines on the **full** `src/**` collectCoverageFrom surface (same denominator as the baseline row).
- **Still large residual 0% / low modules:** `videoCallWindow/video-call-window.ts`, `injected.ts`, `main.ts`, `buildAssets.ts` (CLI asset pipeline), parts of `ui/main/rootWindow` / `serverView`, residual `videoCallWindow/ipc` window-lifecycle branches.
- **Coverage gotcha:** renderer preload specs remain in `COVERAGE_INCOMPATIBLE_SPECS` (Istanbul `EvalError` under electron runner). Prefer main/node harnesses that `require()` preloads so lines still count under `yarn test:coverage`.

## Testing notes (gotchas worth knowing before adding specs)

Expand Down
25 changes: 25 additions & 0 deletions docs/KNOWN_ISSUES.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,28 @@
package.json, patches/@kayahr+jest-electron-runner+29.14.0.patch.
- Reference: PR #3452. Failing run 31515022528 (`check (macos-latest)`); green after the fix
in run 31515403323 (macOS 16m32s, 1721 tests, 0 failures).

## Rocket.Chat presence Meteor methods: `UserPresence:setDefaultStatus` deprecated for 9.0.0

- Status: Not a blocker. We use the non-deprecated `setUserStatus` instead, so nothing is
scheduled to break — this entry exists so the constraint is not rediscovered.
- Detail: `apps/meteor/server/meteor-methods/users/userPresence.ts` logs
`methodDeprecationLogger.method('UserPresence:setDefaultStatus', '9.0.0', '/v1/users.setStatus')`.
The method still works on 8.x but is slated for removal in Rocket.Chat 9.0.0, with the REST
endpoint `/v1/users.setStatus` named as its replacement.
- Why we did not use it anyway: it takes `status` only, so it cannot set a custom status
message. `Meteor.call('setUserStatus', statusType, statusText)`
(`apps/meteor/server/meteor-methods/users/setUserStatus.ts`) sets both in one call, is not
deprecated, and throws real errors (`error-not-allowed`,
`error-status-not-allowed`) instead of returning `undefined` silently. It is also on the
DDP fast-path bypass list (`apps/meteor/client/meteor/overrides/ddpOverREST.ts`), so there
is no latency penalty versus the deprecated method.
- Gotcha to remember: `setUserStatus` is rate limited to **1 call/sec/user**
(`RateLimiter.limitMethod('setUserStatus', 1, 1000, ...)`). Any UI that drives it — e.g.
the tray presence selector — must debounce or the second rapid pick is rejected.
- Second gotcha: `statusDefault` does NOT track these writes. Both methods route through
`Presence.setStatus`, which moves `status`; `statusDefault` stays put. Read `status` for
anything user-visible, including a "currently selected" checkmark.
- Affected files: src/injected.ts (presence read/write bridge), src/ui/main/trayIcon.ts.
- Reference: CORE-2525. Verified against Rocket.Chat @ 8.8.0-develop source and confirmed
live against open.rocket.chat (server 8.8).
Loading
Loading