feat: Options tab in popup + Safari build script
Popup: - Added 5th "Options" tab with key settings accessible without opening the full options page: secret scanning, auto-detect PPI, auto-redact, show highlights, document scan preview - "Open Full Options Page" button for sync/encryption/org/import - Settings changes in popup immediately broadcast to content scripts Safari: - Added build-safari.sh that uses Apple's safari-web-extension-converter to generate an Xcode project from the Firefox build - browser-polyfill.js already handles Safari (uses browser.* like Firefox) https://claude.ai/code/session_01SWSwDfMVij53bCTNSCLMwn
This commit is contained in:
Executable
+66
@@ -0,0 +1,66 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
#
|
||||||
|
# Convert Silent Send for Safari using Apple's safari-web-extension-converter.
|
||||||
|
#
|
||||||
|
# Prerequisites:
|
||||||
|
# - macOS with Xcode installed (free from Mac App Store)
|
||||||
|
# - Apple Developer account ($99/year) for App Store distribution
|
||||||
|
# - Xcode Command Line Tools: xcode-select --install
|
||||||
|
#
|
||||||
|
# This script:
|
||||||
|
# 1. Builds the Firefox variant (Safari uses browser.* API like Firefox)
|
||||||
|
# 2. Runs safari-web-extension-converter to generate an Xcode project
|
||||||
|
# 3. The Xcode project can then be built, tested, and submitted to App Store
|
||||||
|
#
|
||||||
|
|
||||||
|
set -e
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
|
||||||
|
OUT_DIR="$SCRIPT_DIR/safari-build"
|
||||||
|
|
||||||
|
# Check for Xcode
|
||||||
|
if ! command -v xcrun &> /dev/null; then
|
||||||
|
echo "Error: Xcode is required. Install from the Mac App Store."
|
||||||
|
echo "Then run: xcode-select --install"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! xcrun --find safari-web-extension-converter &> /dev/null; then
|
||||||
|
echo "Error: safari-web-extension-converter not found."
|
||||||
|
echo "Make sure Xcode is installed and up to date."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Build the Firefox variant (Safari uses browser.* API)
|
||||||
|
echo "Building extension..."
|
||||||
|
"$SCRIPT_DIR/build.sh" firefox
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Converting for Safari..."
|
||||||
|
|
||||||
|
# Remove previous build
|
||||||
|
rm -rf "$OUT_DIR"
|
||||||
|
|
||||||
|
# Convert — generates an Xcode project
|
||||||
|
xcrun safari-web-extension-converter \
|
||||||
|
"$SCRIPT_DIR/dist/firefox" \
|
||||||
|
--project-location "$OUT_DIR" \
|
||||||
|
--app-name "Silent Send" \
|
||||||
|
--bundle-identifier "com.silentsend.extension" \
|
||||||
|
--swift \
|
||||||
|
--macos-only \
|
||||||
|
--no-open
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Safari project created at: $OUT_DIR"
|
||||||
|
echo ""
|
||||||
|
echo "Next steps:"
|
||||||
|
echo " 1. Open $OUT_DIR/Silent Send.xcodeproj in Xcode"
|
||||||
|
echo " 2. Select your Apple Developer Team in Signing & Capabilities"
|
||||||
|
echo " 3. Build and run (Cmd+R) to test in Safari"
|
||||||
|
echo " 4. To distribute: Product → Archive → Distribute App"
|
||||||
|
echo ""
|
||||||
|
echo "For App Store submission, you'll also need:"
|
||||||
|
echo " - App Store screenshots (1280x800 for Mac)"
|
||||||
|
echo " - Privacy policy URL"
|
||||||
|
echo " - App description and keywords"
|
||||||
@@ -623,6 +623,35 @@ body {
|
|||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Options tab */
|
||||||
|
.setting-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 8px 0;
|
||||||
|
border-bottom: 1px solid #f3f4f6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-item:last-of-type { border-bottom: none; }
|
||||||
|
|
||||||
|
.setting-label {
|
||||||
|
flex: 1;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-label strong {
|
||||||
|
font-size: 12px;
|
||||||
|
display: block;
|
||||||
|
color: #1f2937;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-label .setting-desc {
|
||||||
|
font-size: 10px;
|
||||||
|
color: #9ca3af;
|
||||||
|
display: block;
|
||||||
|
margin-top: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
/* Footer */
|
/* Footer */
|
||||||
.footer {
|
.footer {
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
|
|||||||
@@ -33,6 +33,7 @@
|
|||||||
<button class="tab" data-tab="mappings">Mappings</button>
|
<button class="tab" data-tab="mappings">Mappings</button>
|
||||||
<button class="tab" data-tab="activity">Activity</button>
|
<button class="tab" data-tab="activity">Activity</button>
|
||||||
<button class="tab" data-tab="test">Test</button>
|
<button class="tab" data-tab="test">Test</button>
|
||||||
|
<button class="tab" data-tab="options">Options</button>
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
<!-- Locked State Overlay -->
|
<!-- Locked State Overlay -->
|
||||||
@@ -193,6 +194,54 @@
|
|||||||
<div class="test-identity-status" id="identityStatus"></div>
|
<div class="test-identity-status" id="identityStatus"></div>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
<!-- Options Tab -->
|
||||||
|
<section class="tab-content" id="tab-options">
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-label">
|
||||||
|
<strong>Secret scanning</strong>
|
||||||
|
<span class="setting-desc">Auto-redact API keys, tokens, passwords, SSNs, credit cards</span>
|
||||||
|
</div>
|
||||||
|
<label class="toggle"><input type="checkbox" id="optSecretScanning" checked><span class="toggle-slider"></span></label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-label">
|
||||||
|
<strong>Auto-detect PPI</strong>
|
||||||
|
<span class="setting-desc">Warn about unconfigured personal data (IPs, addresses, paths)</span>
|
||||||
|
</div>
|
||||||
|
<label class="toggle"><input type="checkbox" id="optAutoDetect" checked><span class="toggle-slider"></span></label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-label">
|
||||||
|
<strong>Auto-redact detected PPI</strong>
|
||||||
|
<span class="setting-desc">Replace detected PPI with placeholders on send</span>
|
||||||
|
</div>
|
||||||
|
<label class="toggle"><input type="checkbox" id="optAutoRedact" checked><span class="toggle-slider"></span></label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-label">
|
||||||
|
<strong>Show highlights</strong>
|
||||||
|
<span class="setting-desc">Highlight substituted values in AI responses</span>
|
||||||
|
</div>
|
||||||
|
<label class="toggle"><input type="checkbox" id="optHighlights"><span class="toggle-slider"></span></label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="setting-item">
|
||||||
|
<div class="setting-label">
|
||||||
|
<strong>Document scan preview</strong>
|
||||||
|
<span class="setting-desc">Show PPI findings before uploading documents</span>
|
||||||
|
</div>
|
||||||
|
<label class="toggle"><input type="checkbox" id="optDocPreview" checked><span class="toggle-slider"></span></label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-top:12px;padding-top:10px;border-top:1px solid #e5e7eb">
|
||||||
|
<button class="btn" id="btnOpenFullOptions" style="width:100%;font-size:12px">Open Full Options Page</button>
|
||||||
|
<p class="help-text" style="margin-top:6px;text-align:center">Sync, encryption, org, version history, import/export, and more</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
<!-- Footer -->
|
<!-- Footer -->
|
||||||
<footer class="footer">
|
<footer class="footer">
|
||||||
<div class="privacy-note">
|
<div class="privacy-note">
|
||||||
|
|||||||
+29
-1
@@ -208,12 +208,40 @@ async function initUnlockedUI() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// Options link
|
// Options link (footer)
|
||||||
$('#btnOptions').addEventListener('click', (e) => {
|
$('#btnOptions').addEventListener('click', (e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
api.runtime.openOptionsPage();
|
api.runtime.openOptionsPage();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Options tab
|
||||||
|
$('#btnOpenFullOptions').addEventListener('click', () => {
|
||||||
|
api.runtime.openOptionsPage();
|
||||||
|
});
|
||||||
|
|
||||||
|
// Load options tab settings
|
||||||
|
$('#optSecretScanning').checked = settings.secretScanning !== false;
|
||||||
|
$('#optAutoDetect').checked = settings.autoDetect !== false;
|
||||||
|
$('#optAutoRedact').checked = settings.autoRedactDetected !== false;
|
||||||
|
$('#optHighlights').checked = settings.showHighlights || false;
|
||||||
|
$('#optDocPreview').checked = settings.docScanPreview !== false;
|
||||||
|
|
||||||
|
// Options tab change handlers
|
||||||
|
const optHandlers = [
|
||||||
|
['optSecretScanning', 'secretScanning'],
|
||||||
|
['optAutoDetect', 'autoDetect'],
|
||||||
|
['optAutoRedact', 'autoRedactDetected'],
|
||||||
|
['optHighlights', 'showHighlights'],
|
||||||
|
['optDocPreview', 'docScanPreview'],
|
||||||
|
];
|
||||||
|
for (const [id, key] of optHandlers) {
|
||||||
|
$(`#${id}`).addEventListener('change', async (e) => {
|
||||||
|
settings[key] = e.target.checked;
|
||||||
|
await Storage.saveSettings({ [key]: e.target.checked });
|
||||||
|
api.runtime.sendMessage({ type: 'update:settings', settings });
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
// Update privacy note based on encryption state
|
// Update privacy note based on encryption state
|
||||||
const encEnabled = await Storage._isAtRestEncryptionEnabled();
|
const encEnabled = await Storage._isAtRestEncryptionEnabled();
|
||||||
const encNote = $('#privacyEncNote');
|
const encNote = $('#privacyEncNote');
|
||||||
|
|||||||
Reference in New Issue
Block a user