feat: add abs alias, refresh extension panel, and bump to 0.16.1-fork.1
This commit is contained in:
@@ -1,119 +1,192 @@
|
||||
:root {
|
||||
color-scheme: light dark;
|
||||
--bg: #111827;
|
||||
--card: #1f2937;
|
||||
--border: #374151;
|
||||
--text: #f9fafb;
|
||||
--muted: #d1d5db;
|
||||
--accent: #22c55e;
|
||||
--bg: #0d1117;
|
||||
--panel: #161b22;
|
||||
--item-bg: rgba(255, 255, 255, 0.03);
|
||||
--item-hover: rgba(255, 255, 255, 0.06);
|
||||
--border: #30363d;
|
||||
--border-focus: #484f58;
|
||||
--text-main: #e6edf3;
|
||||
--text-muted: #848d97;
|
||||
--accent: #2f81f7;
|
||||
--accent-soft: rgba(47, 129, 247, 0.1);
|
||||
--success: #3fb950;
|
||||
--warning: #d29922;
|
||||
--font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
}
|
||||
|
||||
body {
|
||||
margin: 0;
|
||||
padding: 12px;
|
||||
background: radial-gradient(circle at top left, #1f2937, #111827);
|
||||
color: var(--text);
|
||||
font:
|
||||
13px/1.5 -apple-system,
|
||||
BlinkMacSystemFont,
|
||||
'Segoe UI',
|
||||
sans-serif;
|
||||
padding: 20px;
|
||||
background-color: var(--bg);
|
||||
color: var(--text-main);
|
||||
font-family: var(--font-family);
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
/* Scrollbar */
|
||||
::-webkit-scrollbar { width: 8px; }
|
||||
::-webkit-scrollbar-track { background: transparent; }
|
||||
::-webkit-scrollbar-thumb { background: var(--border); border-radius: 10px; }
|
||||
::-webkit-scrollbar-thumb:hover { background: var(--border-focus); }
|
||||
|
||||
header {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 10px;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size: 15px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
button {
|
||||
background: #0f172a;
|
||||
color: var(--text);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
padding: 6px 8px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
border-color: var(--accent);
|
||||
h1::before {
|
||||
content: "";
|
||||
display: block;
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background: var(--accent);
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 0 10px var(--accent);
|
||||
}
|
||||
|
||||
.actions {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
button {
|
||||
all: unset;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 6px 12px;
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
color: var(--text-main);
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
button:hover {
|
||||
background: var(--item-hover);
|
||||
border-color: var(--border-focus);
|
||||
}
|
||||
|
||||
#refresh-btn {
|
||||
background: var(--accent);
|
||||
border-color: var(--accent);
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
#refresh-btn:hover {
|
||||
background: #4493f8;
|
||||
border-color: #4493f8;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: color-mix(in srgb, var(--card) 88%, #000 12%);
|
||||
background: var(--panel);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 10px;
|
||||
padding: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding: 16px;
|
||||
margin-bottom: 20px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
h3 {
|
||||
font-size: 12px;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.05em;
|
||||
color: var(--text-muted);
|
||||
margin: 0 0 12px 0;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.stack {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 10px;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.session-title {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
margin-bottom: 6px;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.session-title strong {
|
||||
.session-title h4 {
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
margin: 0;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.tags {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 6px;
|
||||
margin: 6px 0;
|
||||
gap: 4px;
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.tag {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 999px;
|
||||
padding: 2px 7px;
|
||||
color: var(--muted);
|
||||
font-size: 12px;
|
||||
background: var(--accent-soft);
|
||||
color: var(--accent);
|
||||
border: 1px solid transparent;
|
||||
padding: 1px 6px;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.list {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 4px;
|
||||
gap: 1px; /* Divider effect */
|
||||
background: var(--border);
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.item {
|
||||
border: 1px solid color-mix(in srgb, var(--border) 85%, #000 15%);
|
||||
border-radius: 8px;
|
||||
padding: 6px 7px;
|
||||
background: rgba(255, 255, 255, 0.03);
|
||||
overflow: hidden;
|
||||
background: var(--panel);
|
||||
padding: 10px 12px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 2px;
|
||||
transition: background 0.1s ease;
|
||||
}
|
||||
|
||||
.item:hover {
|
||||
background: var(--item-hover);
|
||||
}
|
||||
|
||||
.item-title {
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
color: var(--text-main);
|
||||
}
|
||||
|
||||
.item-url {
|
||||
color: var(--muted);
|
||||
color: var(--text-muted);
|
||||
font-size: 11px;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
@@ -122,9 +195,45 @@ button:hover {
|
||||
|
||||
.row-actions {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
gap: 4px;
|
||||
margin-top: 12px;
|
||||
padding-top: 12px;
|
||||
border-top: 1px solid var(--border);
|
||||
}
|
||||
|
||||
.row-actions button {
|
||||
padding: 4px 8px;
|
||||
font-size: 11px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.empty {
|
||||
color: var(--muted);
|
||||
color: var(--text-muted);
|
||||
text-align: center;
|
||||
padding: 40px 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
||||
background: rgba(110, 118, 129, 0.4);
|
||||
padding: 2px 5px;
|
||||
border-radius: 4px;
|
||||
font-size: 11px;
|
||||
}
|
||||
|
||||
#summary code {
|
||||
color: var(--warning);
|
||||
}
|
||||
|
||||
/* Summary Grid */
|
||||
#summary .tags {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
#summary .tag {
|
||||
text-align: center;
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
@@ -16,86 +16,89 @@ function createTag(text) {
|
||||
}
|
||||
|
||||
function renderSummary(state) {
|
||||
summaryEl.innerHTML = '';
|
||||
summaryEl.innerHTML = '<h3>Overview</h3>';
|
||||
const options = state.options || {};
|
||||
const title = document.createElement('div');
|
||||
title.innerHTML = `<strong>Overview</strong> · extensionId: <code>${state.extensionId}</code>`;
|
||||
|
||||
const idInfo = document.createElement('div');
|
||||
idInfo.style.marginBottom = '12px';
|
||||
idInfo.style.fontSize = '11px';
|
||||
idInfo.style.color = 'var(--text-muted)';
|
||||
idInfo.innerHTML = `Extension ID: <code>${state.extensionId}</code>`;
|
||||
summaryEl.appendChild(idInfo);
|
||||
|
||||
const tags = document.createElement('div');
|
||||
tags.className = 'tags';
|
||||
tags.appendChild(createTag(`sessions: ${state.totals.sessions}`));
|
||||
tags.appendChild(createTag(`tabs: ${state.totals.tabs}`));
|
||||
tags.appendChild(createTag(`Sessions: ${state.totals.sessions}`));
|
||||
tags.appendChild(createTag(`Tabs: ${state.totals.tabs}`));
|
||||
tags.appendChild(
|
||||
createTag(`isolation: ${options.strictWindowIsolation === false ? 'off' : 'on'}`)
|
||||
createTag(`Isolation: ${options.strictWindowIsolation === false ? 'Off' : 'On'}`)
|
||||
);
|
||||
tags.appendChild(
|
||||
createTag(`activation-guard: ${options.suppressCrossWindowActivation === false ? 'off' : 'on'}`)
|
||||
createTag(`Guard: ${options.suppressCrossWindowActivation === false ? 'Off' : 'On'}`)
|
||||
);
|
||||
tags.appendChild(
|
||||
createTag(`auto-clean: ${options.autoCleanEmptyGroups === false ? 'off' : 'on'}`)
|
||||
createTag(`Auto-Clean: ${options.autoCleanEmptyGroups === false ? 'Off' : 'On'}`)
|
||||
);
|
||||
|
||||
const optionActions = document.createElement('div');
|
||||
optionActions.className = 'row-actions';
|
||||
|
||||
const isolationBtn = document.createElement('button');
|
||||
isolationBtn.type = 'button';
|
||||
isolationBtn.textContent =
|
||||
options.strictWindowIsolation === false ? 'Enable Isolation' : 'Disable Isolation';
|
||||
isolationBtn.addEventListener('click', async () => {
|
||||
await send({
|
||||
type: 'AB_PANEL_SET_OPTIONS',
|
||||
options: { ...options, strictWindowIsolation: options.strictWindowIsolation === false },
|
||||
});
|
||||
await refresh();
|
||||
});
|
||||
const createOptionBtn = (text, active, onClick) => {
|
||||
const btn = document.createElement('button');
|
||||
btn.type = 'button';
|
||||
btn.textContent = text;
|
||||
if (active) btn.style.borderColor = 'var(--accent)';
|
||||
btn.addEventListener('click', onClick);
|
||||
return btn;
|
||||
};
|
||||
|
||||
const guardBtn = document.createElement('button');
|
||||
guardBtn.type = 'button';
|
||||
guardBtn.textContent =
|
||||
options.suppressCrossWindowActivation === false ? 'Enable Guard' : 'Disable Guard';
|
||||
guardBtn.addEventListener('click', async () => {
|
||||
await send({
|
||||
type: 'AB_PANEL_SET_OPTIONS',
|
||||
options: {
|
||||
...options,
|
||||
suppressCrossWindowActivation: options.suppressCrossWindowActivation === false,
|
||||
},
|
||||
});
|
||||
await refresh();
|
||||
});
|
||||
optionActions.appendChild(
|
||||
createOptionBtn('Strict Isolation', options.strictWindowIsolation !== false, async () => {
|
||||
await send({
|
||||
type: 'AB_PANEL_SET_OPTIONS',
|
||||
options: { ...options, strictWindowIsolation: options.strictWindowIsolation === false },
|
||||
});
|
||||
await refresh();
|
||||
})
|
||||
);
|
||||
|
||||
const autoCleanBtn = document.createElement('button');
|
||||
autoCleanBtn.type = 'button';
|
||||
autoCleanBtn.textContent =
|
||||
options.autoCleanEmptyGroups === false ? 'Enable Auto-Clean' : 'Disable Auto-Clean';
|
||||
autoCleanBtn.addEventListener('click', async () => {
|
||||
await send({
|
||||
type: 'AB_PANEL_SET_OPTIONS',
|
||||
options: {
|
||||
...options,
|
||||
autoCleanEmptyGroups: options.autoCleanEmptyGroups === false,
|
||||
},
|
||||
});
|
||||
await refresh();
|
||||
});
|
||||
optionActions.appendChild(
|
||||
createOptionBtn('Activation Guard', options.suppressCrossWindowActivation !== false, async () => {
|
||||
await send({
|
||||
type: 'AB_PANEL_SET_OPTIONS',
|
||||
options: {
|
||||
...options,
|
||||
suppressCrossWindowActivation: options.suppressCrossWindowActivation === false,
|
||||
},
|
||||
});
|
||||
await refresh();
|
||||
})
|
||||
);
|
||||
|
||||
optionActions.appendChild(isolationBtn);
|
||||
optionActions.appendChild(guardBtn);
|
||||
optionActions.appendChild(autoCleanBtn);
|
||||
optionActions.appendChild(
|
||||
createOptionBtn('Auto-Clean', options.autoCleanEmptyGroups !== false, async () => {
|
||||
await send({
|
||||
type: 'AB_PANEL_SET_OPTIONS',
|
||||
options: {
|
||||
...options,
|
||||
autoCleanEmptyGroups: options.autoCleanEmptyGroups === false,
|
||||
},
|
||||
});
|
||||
await refresh();
|
||||
})
|
||||
);
|
||||
|
||||
summaryEl.appendChild(title);
|
||||
summaryEl.appendChild(tags);
|
||||
summaryEl.appendChild(optionActions);
|
||||
}
|
||||
|
||||
function renderSessions(state) {
|
||||
sessionsEl.innerHTML = '';
|
||||
sessionsEl.innerHTML = '<h3>Active Sessions</h3>';
|
||||
|
||||
if (!state.sessions || state.sessions.length === 0) {
|
||||
const empty = document.createElement('div');
|
||||
empty.className = 'card empty';
|
||||
empty.textContent = 'No managed sessions yet.';
|
||||
empty.textContent = 'No active sessions monitored.';
|
||||
sessionsEl.appendChild(empty);
|
||||
return;
|
||||
}
|
||||
@@ -107,31 +110,71 @@ function renderSessions(state) {
|
||||
const titleRow = document.createElement('div');
|
||||
titleRow.className = 'session-title';
|
||||
|
||||
const titleLeft = document.createElement('strong');
|
||||
const titleLeft = document.createElement('h4');
|
||||
titleLeft.textContent = session.session;
|
||||
|
||||
const actions = document.createElement('div');
|
||||
actions.className = 'row-actions';
|
||||
|
||||
const focusBtn = document.createElement('button');
|
||||
focusBtn.type = 'button';
|
||||
focusBtn.textContent = 'Focus';
|
||||
focusBtn.addEventListener('click', async () => {
|
||||
await send({ type: 'AB_PANEL_FOCUS_SESSION', session: session.session });
|
||||
await refresh();
|
||||
});
|
||||
|
||||
titleRow.appendChild(titleLeft);
|
||||
titleRow.appendChild(focusBtn);
|
||||
|
||||
const tags = document.createElement('div');
|
||||
tags.className = 'tags';
|
||||
tags.appendChild(createTag(`Window: ${session.windowId ?? 'N/A'}`));
|
||||
tags.appendChild(createTag(`Tabs: ${session.tabs.length}`));
|
||||
|
||||
if (session.group) {
|
||||
tags.appendChild(createTag(`G: ${session.group.title || 'Untitled'}`));
|
||||
}
|
||||
|
||||
if (session.allowedDomains && session.allowedDomains.length > 0) {
|
||||
tags.appendChild(createTag(`Allowlist: ${session.allowedDomains.length} domains`));
|
||||
}
|
||||
|
||||
const list = document.createElement('div');
|
||||
list.className = 'list';
|
||||
for (const tab of session.tabs.slice(0, 10)) {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'item';
|
||||
|
||||
const t = document.createElement('div');
|
||||
t.className = 'item-title';
|
||||
if (tab.active) {
|
||||
const dot = document.createElement('span');
|
||||
dot.textContent = '●';
|
||||
dot.style.color = 'var(--success)';
|
||||
dot.style.marginRight = '6px';
|
||||
dot.style.fontSize = '10px';
|
||||
t.appendChild(dot);
|
||||
}
|
||||
t.appendChild(document.createTextNode(tab.title || '(Untitled)'));
|
||||
|
||||
const u = document.createElement('div');
|
||||
u.className = 'item-url';
|
||||
u.textContent = tab.url || 'about:blank';
|
||||
|
||||
item.appendChild(t);
|
||||
item.appendChild(u);
|
||||
list.appendChild(item);
|
||||
}
|
||||
|
||||
const footerActions = document.createElement('div');
|
||||
footerActions.className = 'row-actions';
|
||||
|
||||
const keepBtn = document.createElement('button');
|
||||
keepBtn.type = 'button';
|
||||
keepBtn.textContent = 'Keep Only This';
|
||||
keepBtn.textContent = 'Isolate Session';
|
||||
keepBtn.addEventListener('click', async () => {
|
||||
await send({ type: 'AB_PANEL_CLOSE_OTHER_SESSION_TABS', session: session.session });
|
||||
await refresh();
|
||||
});
|
||||
|
||||
const policyBtn = document.createElement('button');
|
||||
policyBtn.type = 'button';
|
||||
policyBtn.textContent = 'Set Allowlist';
|
||||
policyBtn.textContent = 'Config Policy';
|
||||
policyBtn.addEventListener('click', async () => {
|
||||
const current = (session.allowedDomains || []).join(',');
|
||||
const input = window.prompt('Allowed domains (comma-separated)', current);
|
||||
@@ -144,62 +187,19 @@ function renderSessions(state) {
|
||||
await refresh();
|
||||
});
|
||||
|
||||
actions.appendChild(focusBtn);
|
||||
actions.appendChild(keepBtn);
|
||||
actions.appendChild(policyBtn);
|
||||
|
||||
titleRow.appendChild(titleLeft);
|
||||
titleRow.appendChild(actions);
|
||||
|
||||
const tags = document.createElement('div');
|
||||
tags.className = 'tags';
|
||||
tags.appendChild(createTag(`window: ${session.windowId ?? 'n/a'}`));
|
||||
tags.appendChild(createTag(`tabs: ${session.tabs.length}`));
|
||||
|
||||
if (session.group) {
|
||||
tags.appendChild(createTag(`group: ${session.group.title || session.group.id}`));
|
||||
tags.appendChild(createTag(`color: ${session.group.color}`));
|
||||
tags.appendChild(createTag(`collapsed: ${session.group.collapsed}`));
|
||||
}
|
||||
|
||||
if (session.allowedDomains && session.allowedDomains.length > 0) {
|
||||
tags.appendChild(createTag(`allowlist: ${session.allowedDomains.join(', ')}`));
|
||||
}
|
||||
|
||||
if (session.riskHints && session.riskHints.length > 0) {
|
||||
for (const hint of session.riskHints) {
|
||||
tags.appendChild(createTag(`risk: ${hint}`));
|
||||
}
|
||||
}
|
||||
|
||||
const list = document.createElement('div');
|
||||
list.className = 'list';
|
||||
for (const tab of session.tabs.slice(0, 10)) {
|
||||
const item = document.createElement('div');
|
||||
item.className = 'item';
|
||||
|
||||
const t = document.createElement('div');
|
||||
t.className = 'item-title';
|
||||
t.textContent = `${tab.active ? '● ' : ''}${tab.title || '(untitled)'}`;
|
||||
|
||||
const u = document.createElement('div');
|
||||
u.className = 'item-url';
|
||||
u.textContent = tab.url || 'about:blank';
|
||||
|
||||
item.appendChild(t);
|
||||
item.appendChild(u);
|
||||
list.appendChild(item);
|
||||
}
|
||||
footerActions.appendChild(keepBtn);
|
||||
footerActions.appendChild(policyBtn);
|
||||
|
||||
card.appendChild(titleRow);
|
||||
card.appendChild(tags);
|
||||
card.appendChild(list);
|
||||
card.appendChild(footerActions);
|
||||
sessionsEl.appendChild(card);
|
||||
}
|
||||
}
|
||||
|
||||
function renderDownloads(state) {
|
||||
downloadsEl.innerHTML = '<strong>Recent Downloads</strong>';
|
||||
downloadsEl.innerHTML = '<h3>Recent Downloads</h3>';
|
||||
|
||||
const list = document.createElement('div');
|
||||
list.className = 'list';
|
||||
|
||||
Reference in New Issue
Block a user