Skip to content

Add Safari extension support#26

Open
ynot0083 wants to merge 3 commits into
jason5ng32:mainfrom
ynot0083:codex/add-safari-extension-support
Open

Add Safari extension support#26
ynot0083 wants to merge 3 commits into
jason5ng32:mainfrom
ynot0083:codex/add-safari-extension-support

Conversation

@ynot0083
Copy link
Copy Markdown

@ynot0083 ynot0083 commented May 5, 2026

Summary

  • add Safari build and packaging scripts with manifest post-processing
  • make Apple direct streaming the zero-config default when no proxy host is configured
  • add browser compatibility fallbacks for Safari unsupported APIs and update docs/locales

Validation

  • npm run build:safari

Notes

The generated Xcode project is intentionally ignored because it contains local signing settings and can be regenerated with npm run package:safari.

@ynot0083 ynot0083 marked this pull request as ready for review May 5, 2026 13:23
@jason5ng32
Copy link
Copy Markdown
Owner

Thanks for putting this together — the Safari packaging script and the manifest post-processing are clean, and a Safari build is a great direction. Before going further I want to flag a pattern that runs through several of the changes: behavior that should be Safari-specific has been applied globally, which regresses the Chrome build. Same root cause across most of the issues below.

Issues

1. Global default flips affect Chrome users.
src/lib/defaults.js flips showTopSites, reverseProxy, and zenMusic to false. These are reasonable defaults for a Safari self-use build, but they aren't gated — new Chrome Web Store installs will get them too. In particular, reverseProxy: false means new Chrome users hit the sylvan.apple.com cert trust problem on first launch, which is exactly what the proxy default was there to avoid.

2. Favicon regression on Chrome.
faviconUrlFor in src/lib/topsites.js now unconditionally returns an SVG initial-letter placeholder, and the favicon permission is removed from the manifest. Chrome supports _favicon/ and was using it; this PR drops real favicons for Chrome users without need. The Safari packager already strips permissions at packaging time — the runtime code and the Chrome manifest can stay Chrome-friendly.

3. Locale strings rewritten in place rather than added.
All four _locales/*/messages.json files have their existing message values rewritten — ext_description, video_apple_help_intro, video_apple_help_option2_body_*, options_translation_description, options_translation_status_no_api, etc. The Chrome-specific guidance ("click Advanced, then Proceed to sylvan.apple.com (unsafe)", "requires Chrome 138+", "uses Chrome's built-in cache") gets replaced with vaguer browser-agnostic phrasing. For Chrome users this is a regression in help quality — the previous text was concrete and actionable, the new text is generic. The right shape is to keep the Chrome strings as-is and add Safari-tailored variants (either separate keys, or swap the locale files at packaging time in prepare-safari-dist.mjs), not to overwrite the originals.

4. Build-time safety net removed.
vite.config.js had a fail-fast check for VITE_MACIFY_BASE. With it gone, forgetting to set the env var on a Chrome Web Store release silently produces a build that streams direct from Apple — i.e. the Chrome Store version degrades into the Safari self-use experience without anyone noticing. The check should stay for the Chrome path; only the Safari build should treat it as optional.

5. Zen reminder cooldown semantics on Safari.
The original code uses chrome.storage.session specifically so the cooldown resets on browser cold start (otherwise users get a "you've worked 8h" greeting first thing in the morning — there's an explicit comment about this). The new sessionCache falls back to chrome.storage.local on Safari, which persists across restarts and reintroduces exactly that bug. Worth at least documenting; ideally Safari should clear lastZenSessionAt on first new-tab of the session through some other mechanism.

Suggested direction

Issues 1–4 share a root cause: changes that are correct for Safari have been applied globally instead of gated on the build target. The cleanest fix is to introduce a build-target flag and gate the Safari-specific behavior on it, keeping the Chrome path unchanged. Vite's mode is enough:

```js
// package.json
"build": "vite build --mode chrome",
"build:safari": "vite build --mode safari && node scripts/prepare-safari-dist.mjs"
```

```js
// vite.config.js — keep the env check, but only for chrome mode
export default defineConfig(({ mode }) => {
const target = mode === 'safari' ? 'safari' : 'chrome';
if (target === 'chrome') {
// existing REQUIRED_ENV check
}
return {
define: { 'import.meta.env.VITE_BUILD_TARGET': JSON.stringify(target) },
...
};
});
```

Then in code:

```js
const isSafari = import.meta.env.VITE_BUILD_TARGET === 'safari';
// defaults.js
showTopSites: !isSafari,
reverseProxy: !isSafari,
zenMusic: !isSafari,
// topsites.js
if (isSafari) return svgPlaceholder(...);
return chrome.runtime.getURL(`_favicon/?...`);
```

For the locale strings, `prepare-safari-dist.mjs` is a natural place to overlay Safari-specific overrides on top of the Chrome message files at packaging time, so the Chrome locale files don't need to change at all.

@ynot0083
Copy link
Copy Markdown
Author

ynot0083 commented May 6, 2026

Updated per review feedback:

  • Added build-target gating with chrome and safari modes plus a VITE_BUILD_TARGET define.
  • Restored Chrome defaults for top sites, reverse proxy, zen music, favicon permission, and Chrome favicon runtime behavior.
  • Restored the original Chrome locale files; Safari-specific locale text is now overlaid in the Safari packaging script.
  • Kept the VITE_MACIFY_BASE fail-fast check for Chrome builds while allowing Safari builds to omit it.
  • Changed the Safari storage fallback for Zen reminders to in-memory storage instead of persistent local storage, preserving cold-start reset semantics without stale cooldown timestamps.

Validation:

  • npm run build:safari
  • VITE_MACIFY_BASE=https://example.com npm run build
  • npm run build without VITE_MACIFY_BASE fails fast as expected.

@jason5ng32
Copy link
Copy Markdown
Owner

Quick status update on this PR: thanks again for the second pass — all five issues from the previous review were addressed cleanly, and the build-target gating approach is exactly what I had in mind.

Since then, however, main has advanced 9 commits and the branch is now diverged with merge conflicts. One of those commits (951a305) is a substantive rework of the Zen reminder model (active-time tracking + iOS-style snooze), which touches several of the same files this PR modifies — src/lib/zen.svelte.js, src/lib/storage.js, src/components/ZenReminderPill.svelte. So this is more than a textual rebase: the Safari cold-start cooldown reset would need to be re-thought on top of the new model.

Totally understand if that's more work than you signed up for, no pressure either way. If you do want to take another pass, rebasing onto current main and re-evaluating the Zen integration against the new reminder model would be the path forward. Either way, much appreciated for getting it this far — the Safari packaging script and the build-target gating are solid foundations whenever Safari support lands.

@ynot0083 ynot0083 force-pushed the codex/add-safari-extension-support branch from ab0ef8b to 3b27ffd Compare May 8, 2026 16:53
@ynot0083
Copy link
Copy Markdown
Author

ynot0083 commented May 8, 2026

Rebased this PR onto current main and resolved the Zen reminder conflicts.

What changed in this pass:

  • Kept the new active-time reminder model from main, including idle tracking and iOS-style snooze.
  • Routed the new Zen tracking storage through a sessionCache wrapper. Chrome still delegates to chrome.storage.session; Safari builds without storage.session fall back to in-memory state, so stale reminder cooldowns do not persist across browser cold starts.
  • Kept the old ZenReminderPill component deleted; Safari support now integrates through the new zen-reminder and zen-tracking path.
  • Updated Safari manifest post-processing to remove idle along with topSites and favicon, since the Safari build also removes the background worker.
  • Re-checked that Chrome locale files and Chrome manifest behavior remain unchanged, with Safari locale overrides applied only during Safari packaging.

Validation:

  • npm run build:safari
  • Safari dist check: permissions reduced to storage, background removed, Safari description overlaid
  • VITE_MACIFY_BASE=https://example.com npm run build
  • Chrome dist check: storage, topSites, favicon, idle, and background are preserved
  • npm run build without VITE_MACIFY_BASE still fails fast as expected.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants