feat(options,popup,background): add per-page aria2 dir option and popup toggle
- Options: add default toggle and base path () - Popup: allow per-send enable + preview; passes AGENTS.md LICENSE node_modules package-lock.json releases src dist manifest.json package.json README.md scripts to aria2 - Background: quick actions honor default, set AGENTS.md LICENSE node_modules package-lock.json releases src dist manifest.json package.json README.md scripts to <base>/<identifier> - Storage: new prefs and
This commit is contained in:
		
					parent
					
						
							
								56035451df
							
						
					
				
			
			
				commit
				
					
						0d944892d8
					
				
			
		
					 5 changed files with 89 additions and 7 deletions
				
			
		| 
						 | 
				
			
			@ -74,6 +74,8 @@ async function getPrefs() {
 | 
			
		|||
  return {
 | 
			
		||||
    defaultAction: prefs?.defaultAction || 'download',
 | 
			
		||||
    allIncludeMeta: !!prefs?.allIncludeMeta,
 | 
			
		||||
    perPageDirEnabled: !!prefs?.perPageDirEnabled,
 | 
			
		||||
    perPageDirBase: prefs?.perPageDirBase || '/aria2/data',
 | 
			
		||||
  };
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -108,7 +110,7 @@ async function rebuildContextSubmenu(tab) {
 | 
			
		|||
  let items = [];
 | 
			
		||||
  try { items = await collectFromTab(tab.id); } catch (_) { items = []; }
 | 
			
		||||
  const identifier = parseIdentifierFromUrl(tab.url || '');
 | 
			
		||||
  const { defaultAction, allIncludeMeta } = await getPrefs();
 | 
			
		||||
  const { defaultAction, allIncludeMeta, perPageDirEnabled, perPageDirBase } = await getPrefs();
 | 
			
		||||
  const verb = defaultAction === 'copy' ? 'Copy' : 'Download';
 | 
			
		||||
 | 
			
		||||
  const top = computeTopTypes(items, identifier, 4);
 | 
			
		||||
| 
						 | 
				
			
			@ -256,7 +258,7 @@ browser.contextMenus.onClicked.addListener(async (info, tab) => {
 | 
			
		|||
  const action = dynamicActions.get(String(info.menuItemId));
 | 
			
		||||
  if (!action || !tab?.id) return;
 | 
			
		||||
  try {
 | 
			
		||||
    const { defaultAction, allIncludeMeta } = await getPrefs();
 | 
			
		||||
    const { defaultAction, allIncludeMeta, perPageDirEnabled, perPageDirBase } = await getPrefs();
 | 
			
		||||
    const identifier = parseIdentifierFromUrl(tab.url || '');
 | 
			
		||||
    const items = await collectFromTab(tab.id);
 | 
			
		||||
    let selected = items || [];
 | 
			
		||||
| 
						 | 
				
			
			@ -279,7 +281,16 @@ browser.contextMenus.onClicked.addListener(async (info, tab) => {
 | 
			
		|||
  } else {
 | 
			
		||||
      const { endpoint, secret } = await getAria2();
 | 
			
		||||
      if (!endpoint) { console.warn('aria2 endpoint not set'); return; }
 | 
			
		||||
      try { await globalThis.addUrisBatch({ endpoint, secret, uris }); } catch (e) { console.warn('aria2 send failed', e); }
 | 
			
		||||
      // Build aria2 options (dir) based on prefs
 | 
			
		||||
      let options = {};
 | 
			
		||||
      try {
 | 
			
		||||
        if (perPageDirEnabled && perPageDirBase) {
 | 
			
		||||
          const base = String(perPageDirBase).replace(/\/+$/, '');
 | 
			
		||||
          const dir = identifier ? `${base}/${identifier}` : base;
 | 
			
		||||
          if (dir) options.dir = dir;
 | 
			
		||||
        }
 | 
			
		||||
      } catch (_) { /* noop */ }
 | 
			
		||||
      try { await globalThis.addUrisBatch({ endpoint, secret, uris, options }); } catch (e) { console.warn('aria2 send failed', e); }
 | 
			
		||||
      await setBadgeForTab(tab.id, {
 | 
			
		||||
        text: String(count),
 | 
			
		||||
        color: '#3b82f6',
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -41,6 +41,12 @@
 | 
			
		|||
        <div class="row">
 | 
			
		||||
          <label class="chk"><input type="checkbox" id="include-meta" /> Include metadata in “All”</label>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="row">
 | 
			
		||||
          <label class="chk"><input type="checkbox" id="per-page-dir-enabled" /> Use per‑page download directory by default</label>
 | 
			
		||||
        </div>
 | 
			
		||||
        <div class="row">
 | 
			
		||||
          <label>Base directory <input id="per-page-dir-base" placeholder="/aria2/data" /></label>
 | 
			
		||||
        </div>
 | 
			
		||||
      </section>
 | 
			
		||||
    </main>
 | 
			
		||||
    <script type="module" src="index.js"></script>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -5,6 +5,8 @@ const els = {
 | 
			
		|||
  secret: document.getElementById('rpc-secret'),
 | 
			
		||||
  defaultAction: document.getElementById('default-action'),
 | 
			
		||||
  includeMeta: document.getElementById('include-meta'),
 | 
			
		||||
  perPageDirEnabled: document.getElementById('per-page-dir-enabled'),
 | 
			
		||||
  perPageDirBase: document.getElementById('per-page-dir-base'),
 | 
			
		||||
  btnSave: document.getElementById('btn-save'),
 | 
			
		||||
  btnReset: document.getElementById('btn-reset'),
 | 
			
		||||
  btnTest: document.getElementById('btn-test'),
 | 
			
		||||
| 
						 | 
				
			
			@ -19,8 +21,12 @@ async function restore() {
 | 
			
		|||
  const { prefs } = await browser.storage.local.get('prefs');
 | 
			
		||||
  const defaultAction = prefs?.defaultAction || 'download';
 | 
			
		||||
  const includeMeta = !!prefs?.allIncludeMeta;
 | 
			
		||||
  const perPageDirEnabled = !!prefs?.perPageDirEnabled;
 | 
			
		||||
  const perPageDirBase = prefs?.perPageDirBase || '/aria2/data';
 | 
			
		||||
  els.defaultAction.value = defaultAction;
 | 
			
		||||
  els.includeMeta.checked = includeMeta;
 | 
			
		||||
  els.perPageDirEnabled.checked = perPageDirEnabled;
 | 
			
		||||
  els.perPageDirBase.value = perPageDirBase;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function save() {
 | 
			
		||||
| 
						 | 
				
			
			@ -28,9 +34,11 @@ async function save() {
 | 
			
		|||
  const secret = els.secret.value.trim();
 | 
			
		||||
  const defaultAction = els.defaultAction.value;
 | 
			
		||||
  const allIncludeMeta = !!els.includeMeta.checked;
 | 
			
		||||
  const perPageDirEnabled = !!els.perPageDirEnabled.checked;
 | 
			
		||||
  const perPageDirBase = els.perPageDirBase.value.trim() || '/aria2/data';
 | 
			
		||||
  await browser.storage.local.set({
 | 
			
		||||
    aria2: { endpoint, secret },
 | 
			
		||||
    prefs: { defaultAction, allIncludeMeta }
 | 
			
		||||
    prefs: { defaultAction, allIncludeMeta, perPageDirEnabled, perPageDirBase }
 | 
			
		||||
  });
 | 
			
		||||
  els.status.textContent = 'Saved.';
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -49,6 +49,12 @@
 | 
			
		|||
      <div class="row">
 | 
			
		||||
        <label>Secret <input id="rpc-secret" type="password" placeholder="your-token" /></label>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="row">
 | 
			
		||||
        <label class="chk"><input type="checkbox" id="use-perpage-dir" /> Use per‑page download directory</label>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="row">
 | 
			
		||||
        <small id="dir-preview"></small>
 | 
			
		||||
      </div>
 | 
			
		||||
      <div class="row actions">
 | 
			
		||||
        <button id="btn-copy">Copy Links</button>
 | 
			
		||||
        <button id="btn-send">Send to aria2</button>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -15,6 +15,8 @@ const els = {
 | 
			
		|||
  btnSend: document.getElementById('btn-send'),
 | 
			
		||||
  rpcEndpoint: document.getElementById('rpc-endpoint'),
 | 
			
		||||
  rpcSecret: document.getElementById('rpc-secret'),
 | 
			
		||||
  usePerPageDir: document.getElementById('use-perpage-dir'),
 | 
			
		||||
  dirPreview: document.getElementById('dir-preview'),
 | 
			
		||||
  count: document.getElementById('count'),
 | 
			
		||||
  status: document.getElementById('status'),
 | 
			
		||||
  openOptions: document.getElementById('open-options'),
 | 
			
		||||
| 
						 | 
				
			
			@ -42,6 +44,21 @@ async function getActiveTabId() {
 | 
			
		|||
  return tabs[0]?.id;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function parseIdentifierFromUrl(url) {
 | 
			
		||||
  try {
 | 
			
		||||
    const u = new URL(url);
 | 
			
		||||
    const parts = u.pathname.split('/').filter(Boolean);
 | 
			
		||||
    const idx = parts.indexOf('download');
 | 
			
		||||
    if (idx >= 0 && parts[idx + 1]) return parts[idx + 1];
 | 
			
		||||
  } catch (_) {}
 | 
			
		||||
  return '';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function getActiveTabUrl() {
 | 
			
		||||
  const tabs = await browser.tabs.query({ active: true, currentWindow: true });
 | 
			
		||||
  return tabs[0]?.url || '';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function refresh() {
 | 
			
		||||
  els.status.textContent = '';
 | 
			
		||||
  try {
 | 
			
		||||
| 
						 | 
				
			
			@ -58,6 +75,7 @@ async function refresh() {
 | 
			
		|||
    }
 | 
			
		||||
    allItems = items || [];
 | 
			
		||||
    await updateCount();
 | 
			
		||||
    await updateDirPreview();
 | 
			
		||||
  } catch (e) {
 | 
			
		||||
    els.status.textContent = String(e?.message || e);
 | 
			
		||||
  }
 | 
			
		||||
| 
						 | 
				
			
			@ -95,10 +113,21 @@ async function sendToAria2() {
 | 
			
		|||
  // Persist settings
 | 
			
		||||
  await browser.storage.local.set({ aria2: { endpoint, secret } });
 | 
			
		||||
  els.status.textContent = 'Sending to aria2…';
 | 
			
		||||
 | 
			
		||||
  // aria2 options
 | 
			
		||||
  let options = {};
 | 
			
		||||
  try {
 | 
			
		||||
    if (els.usePerPageDir?.checked) {
 | 
			
		||||
      const { prefs } = await browser.storage.local.get('prefs');
 | 
			
		||||
      const base = (prefs?.perPageDirBase || '/aria2/data').replace(/\/+$/, '');
 | 
			
		||||
      const url = await getActiveTabUrl();
 | 
			
		||||
      const id = parseIdentifierFromUrl(url);
 | 
			
		||||
      const dir = id ? `${base}/${id}` : base;
 | 
			
		||||
      if (dir) options.dir = dir;
 | 
			
		||||
    }
 | 
			
		||||
  } catch (_) {}
 | 
			
		||||
  const res = await browser.runtime.sendMessage({
 | 
			
		||||
    type: 'aria2.send',
 | 
			
		||||
    payload: { endpoint, secret, uris }
 | 
			
		||||
    payload: { endpoint, secret, uris, options }
 | 
			
		||||
  });
 | 
			
		||||
  if (res?.ok) {
 | 
			
		||||
    els.status.textContent = `Sent ${uris.length} link(s) to aria2.`;
 | 
			
		||||
| 
						 | 
				
			
			@ -115,6 +144,11 @@ async function restoreSettings() {
 | 
			
		|||
  } else {
 | 
			
		||||
    els.rpcEndpoint.value = 'http://localhost:6800/jsonrpc';
 | 
			
		||||
  }
 | 
			
		||||
  try {
 | 
			
		||||
    const { prefs } = await browser.storage.local.get('prefs');
 | 
			
		||||
    const enabled = !!prefs?.perPageDirEnabled;
 | 
			
		||||
    els.usePerPageDir.checked = enabled;
 | 
			
		||||
  } catch (_) {}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
function wire() {
 | 
			
		||||
| 
						 | 
				
			
			@ -126,6 +160,9 @@ function wire() {
 | 
			
		|||
    els.fType, els.fTypeRe, els.fName, els.fNameRe, els.fSizeMin, els.fSizeMax,
 | 
			
		||||
    els.fDateFrom, els.fDateTo, els.fCase
 | 
			
		||||
  ].forEach(el => el.addEventListener('input', () => { updateCount(); }));
 | 
			
		||||
  if (els.usePerPageDir) {
 | 
			
		||||
    els.usePerPageDir.addEventListener('change', () => { updateDirPreview(); });
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
document.addEventListener('DOMContentLoaded', async () => {
 | 
			
		||||
| 
						 | 
				
			
			@ -150,3 +187,17 @@ function renderPreview() {
 | 
			
		|||
    els.preview.appendChild(li);
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
async function updateDirPreview() {
 | 
			
		||||
  if (!els.dirPreview) return;
 | 
			
		||||
  try {
 | 
			
		||||
    const { prefs } = await browser.storage.local.get('prefs');
 | 
			
		||||
    const base = (prefs?.perPageDirBase || '/aria2/data').replace(/\/+$/, '');
 | 
			
		||||
    const url = await getActiveTabUrl();
 | 
			
		||||
    const id = parseIdentifierFromUrl(url);
 | 
			
		||||
    const dir = id ? `${base}/${id}` : base;
 | 
			
		||||
    els.dirPreview.textContent = els.usePerPageDir?.checked ? `Will set aria2 dir: ${dir}` : '';
 | 
			
		||||
  } catch (_) {
 | 
			
		||||
    els.dirPreview.textContent = '';
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue