Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 14 additions & 3 deletions chrome-extension/background.js
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,21 @@ async function handlePIICheck(text) {
// Pull the latest backendUrl every time — the service worker may have been
// torn down since startup, which resets in-memory `backendUrl` to the
// default even if the user configured a different one in options.
const { backendUrl: storedUrl } = await chrome.storage.sync.get({
const stored = await chrome.storage.sync.get({
backendUrl: DEFAULT_API_BASE,
disabledLabels: [],
allLabels: [],
});
backendUrl = storedUrl || DEFAULT_API_BASE;
backendUrl = stored.backendUrl || DEFAULT_API_BASE;

// Build enabled_labels: all known labels minus the ones the user disabled.
// An empty array is sent when all labels are enabled (backend treats it as "all").
const disabledSet = new Set(stored.disabledLabels || []);
const allLabels = stored.allLabels || [];
const enabledLabels =
allLabels.length > 0 && disabledSet.size > 0
? allLabels.filter((l) => !disabledSet.has(l))
: [];

const url = `${backendUrl}/api/pii/check`;

Expand All @@ -186,7 +197,7 @@ async function handlePIICheck(text) {
response = await fetch(url, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ message: text }),
body: JSON.stringify({ message: text, enabled_labels: enabledLabels }),
signal: AbortSignal.timeout(10000),
});
} catch (e) {
Expand Down
168 changes: 167 additions & 1 deletion chrome-extension/options.css
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,9 @@ body {
min-height: 100vh;
padding: 48px 24px;
display: flex;
justify-content: center;
flex-direction: column;
align-items: center;
gap: 24px;
}

.card {
Expand Down Expand Up @@ -307,3 +309,167 @@ input[type="url"]:focus,
.save-error {
color: var(--err);
}

/* ---------- Multiple cards ---------- */

/* ---------- Label grid (PII types) ---------- */

.label-grid {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
gap: 8px;
margin-bottom: 20px;
}

.label-item {
display: flex;
align-items: center;
gap: 8px;
font-size: 12px;
font-family: var(--mono);
color: var(--text);
cursor: pointer;
padding: 6px 8px;
border-radius: var(--radius-sm);
background: var(--bg-subtle);
user-select: none;
}

.label-item input[type="checkbox"] {
accent-color: var(--brand);
width: 14px;
height: 14px;
flex-shrink: 0;
cursor: pointer;
}

.label-loading {
font-size: 12px;
color: var(--text-muted);
}

/* ---------- Custom patterns ---------- */

.pattern-form {
margin-bottom: 16px;
}

.pattern-form-row {
display: flex;
gap: 8px;
align-items: center;
flex-wrap: wrap;
margin-bottom: 8px;
}

.pattern-form-row .input-mono {
flex: 1;
min-width: 120px;
}

.pattern-form-row .pattern-replacement {
flex: 1.2;
}

.pattern-preview-row {
display: flex;
gap: 8px;
align-items: center;
}

.pattern-preview-row .input-mono {
flex: 1;
}

.pattern-preview-result {
font-size: 12px;
font-family: var(--mono);
color: var(--text-muted);
white-space: nowrap;
}

.pattern-error {
color: var(--err);
min-height: 1.2em;
}

.pattern-list {
display: flex;
flex-direction: column;
gap: 6px;
border-top: 0.5px solid var(--border);
padding-top: 16px;
}

.pattern-row {
display: flex;
align-items: center;
gap: 10px;
padding: 8px 10px;
background: var(--bg-subtle);
border-radius: var(--radius-sm);
font-size: 12px;
flex-wrap: wrap;
}

.pattern-label {
font-family: var(--mono);
font-weight: 600;
color: var(--brand);
flex-shrink: 0;
}

.pattern-regex-val {
font-family: var(--mono);
color: var(--code-text);
flex: 1;
word-break: break-all;
}

.pattern-replacement-val {
font-family: var(--mono);
color: var(--ok);
font-size: 12px;
flex-shrink: 0;
white-space: nowrap;
}

.btn-danger {
padding: 5px 10px;
background: transparent;
color: var(--err);
border: 0.5px solid var(--err);
border-radius: var(--radius-sm);
font-size: 12px;
cursor: pointer;
flex-shrink: 0;
transition: background 0.15s ease;
}

.btn-danger:hover {
background: rgba(226, 75, 74, 0.1);
}

.btn-secondary {
padding: 5px 10px;
background: transparent;
color: var(--text-muted);
border: 0.5px solid var(--border-strong);
border-radius: var(--radius-sm);
font-size: 12px;
cursor: pointer;
flex-shrink: 0;
transition: background 0.15s ease;
}

.btn-secondary:hover {
background: var(--bg-subtle);
}


.pattern-row .input-mono {
padding: 5px 8px;
font-size: 12px;
flex: 1;
min-width: 80px;
}
76 changes: 76 additions & 0 deletions chrome-extension/options.html
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,82 @@
</div>
</div>
</div>

<div class="card" id="pii-types-card">
<header class="card-header">
<div class="card-title">
<span class="card-name">PII entity types</span>
<span class="card-sub">Uncheck types you want to leave unmasked</span>
</div>
<a href="#" id="toggle-all" class="link">Disable all</a>
</header>
<div class="card-body">
<div id="label-grid" class="label-grid">
<span class="label-loading">Loading labels…</span>
</div>
<div class="form-actions">
<button id="save-labels-btn" type="button" class="btn-primary">
Save
</button>
<span id="labels-status" class="save-status" role="status"></span>
</div>
</div>
</div>

<div class="card" id="patterns-card">
<header class="card-header">
<div class="card-title">
<span class="card-name">Custom patterns</span>
<span class="card-sub">Domain-specific regex rules applied on top of the model</span>
</div>
</header>
<div class="card-body">
<form id="pattern-form" class="pattern-form" autocomplete="off">
<div class="pattern-form-row">
<input
type="text"
id="pattern-label"
class="input-mono"
placeholder="LABEL"
title="Name for this entity type (e.g. EMPLOYEE_ID)"
required
/>
<input
type="text"
id="pattern-regex"
class="input-mono"
placeholder="Regex"
title="Regular expression pattern (e.g. EMP-\d{6})"
required
/>
<input
type="text"
id="pattern-replacement"
class="input-mono pattern-replacement"
placeholder="Replacement"
/>
<button type="submit" class="btn-primary">Add</button>
</div>
<p class="form-help" style="margin: -4px 0 8px 0;">
Replacement defaults to [LABEL] if left empty.
</p>
<div class="pattern-preview-row">
<input
type="text"
id="pattern-sample"
class="input-mono"
placeholder="Sample text to test against"
/>
<span id="pattern-preview-result" class="pattern-preview-result"></span>
</div>
<p id="pattern-regex-error" class="form-help pattern-error"></p>
</form>

<div id="pattern-list" class="pattern-list">
<span class="label-loading">Loading patterns…</span>
</div>
</div>
</div>
</div>
<script src="config.js"></script>
<script src="options.js"></script>
Expand Down
Loading
Loading