fix(ext/popup): auto-popout for attachment types, keep login/note in popup
- Login and secure_note types stay in popup without attachment UI - All other types (identity, card, key, totp, document) auto-redirect to full tab when selected - Attachments only shown for login/secure_note when opened in tab Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
5
.claude/settings.json
Normal file
5
.claude/settings.json
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"enabledPlugins": {
|
||||
"superpowers@claude-plugins-official": true
|
||||
}
|
||||
}
|
||||
@@ -78,7 +78,11 @@ function renderTypeSelection(app: HTMLElement): void {
|
||||
btn.addEventListener('click', () => {
|
||||
const type = btn.dataset.type as ItemType;
|
||||
setState({ newType: type });
|
||||
renderItemForm(app, 'add');
|
||||
if (type === 'login' || type === 'secure_note') {
|
||||
renderItemForm(app, 'add');
|
||||
} else {
|
||||
popOutToTab();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/// Login type detail + form. Reference implementation for the shared
|
||||
/// field helpers introduced in Slice 2.
|
||||
|
||||
import { getState, setState, sendMessage, navigate, escapeHtml, popOutToTab } from '../../popup';
|
||||
import { getState, setState, sendMessage, navigate, escapeHtml, popOutToTab, isInTab } from '../../popup';
|
||||
import type { Item, ItemId, LoginCore, ManifestEntry, Section, TotpConfig, AttachmentRef } from '../../../shared/types';
|
||||
import { DEFAULT_PASSWORD_REQUEST } from '../../../shared/types';
|
||||
import { base32Decode, base32Encode } from '../../../shared/base32';
|
||||
@@ -267,7 +267,7 @@ export function renderForm(app: HTMLElement, mode: 'add' | 'edit', existing: Ite
|
||||
<div class="form-group"><label class="label" for="f-notes">notes</label>
|
||||
<textarea id="f-notes" placeholder="recovery codes, security questions...">${escapeHtml(notes)}</textarea></div>
|
||||
${renderSectionsEditor(sectionsDraft, sectionsExpanded)}
|
||||
${renderAttachmentsDisclosure({ itemId: existing?.id ?? '', attachments: attachmentsDraft, mode: 'edit' })}
|
||||
${isInTab() ? renderAttachmentsDisclosure({ itemId: existing?.id ?? '', attachments: attachmentsDraft, mode: 'edit' }) : ''}
|
||||
<div class="form-actions">
|
||||
<button class="btn" id="cancel-btn">cancel</button>
|
||||
<button class="btn btn-primary" id="save-btn">${state.loading ? '<span class="spinner"></span>' : 'save'}</button>
|
||||
@@ -284,26 +284,28 @@ export function renderForm(app: HTMLElement, mode: 'add' | 'edit', existing: Ite
|
||||
};
|
||||
wireSectionsEditor(app, sectionsDraft, rerender);
|
||||
|
||||
const wireDisclosure = (): void => {
|
||||
wireAttachmentsDisclosure(app, {
|
||||
itemId: existing?.id ?? '',
|
||||
attachments: attachmentsDraft,
|
||||
mode: 'edit',
|
||||
onChange: (next) => {
|
||||
attachmentsDraft = next;
|
||||
const disc = app.querySelector('.attachments-disclosure');
|
||||
if (disc) {
|
||||
disc.outerHTML = renderAttachmentsDisclosure({
|
||||
itemId: existing?.id ?? '',
|
||||
attachments: attachmentsDraft,
|
||||
mode: 'edit',
|
||||
});
|
||||
wireDisclosure();
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
wireDisclosure();
|
||||
if (isInTab()) {
|
||||
const wireDisclosure = (): void => {
|
||||
wireAttachmentsDisclosure(app, {
|
||||
itemId: existing?.id ?? '',
|
||||
attachments: attachmentsDraft,
|
||||
mode: 'edit',
|
||||
onChange: (next) => {
|
||||
attachmentsDraft = next;
|
||||
const disc = app.querySelector('.attachments-disclosure');
|
||||
if (disc) {
|
||||
disc.outerHTML = renderAttachmentsDisclosure({
|
||||
itemId: existing?.id ?? '',
|
||||
attachments: attachmentsDraft,
|
||||
mode: 'edit',
|
||||
});
|
||||
wireDisclosure();
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
wireDisclosure();
|
||||
}
|
||||
|
||||
document.getElementById('gen-btn')?.addEventListener('click', (e) => {
|
||||
const trigger = e.currentTarget as HTMLElement;
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
/// SecureNote: a single multiline body field. Concealed by default in the
|
||||
/// detail view; the form is just a big <textarea>.
|
||||
|
||||
import { getState, setState, sendMessage, navigate, escapeHtml, popOutToTab } from '../../popup';
|
||||
import { getState, setState, sendMessage, navigate, escapeHtml, popOutToTab, isInTab } from '../../popup';
|
||||
import type { Item, ItemId, ManifestEntry, Section, AttachmentRef } from '../../../shared/types';
|
||||
import {
|
||||
renderConcealedRow, renderSignatureBlock, wireFieldHandlers, renderSections,
|
||||
@@ -122,7 +122,7 @@ export function renderForm(app: HTMLElement, mode: 'add' | 'edit', existing: Ite
|
||||
<div class="form-group"><label class="label" for="f-body">body</label>
|
||||
<textarea id="f-body" rows="10" placeholder="paste secrets here">${escapeHtml(body)}</textarea></div>
|
||||
${renderSectionsEditor(sectionsDraft, sectionsExpanded)}
|
||||
${renderAttachmentsDisclosure({ itemId: existing?.id ?? '', attachments: attachmentsDraft, mode: 'edit' })}
|
||||
${isInTab() ? renderAttachmentsDisclosure({ itemId: existing?.id ?? '', attachments: attachmentsDraft, mode: 'edit' }) : ''}
|
||||
<div class="form-actions">
|
||||
<button class="btn" id="cancel-btn">cancel</button>
|
||||
<button class="btn btn-primary" id="save-btn">save</button>
|
||||
@@ -139,26 +139,28 @@ export function renderForm(app: HTMLElement, mode: 'add' | 'edit', existing: Ite
|
||||
};
|
||||
wireSectionsEditor(app, sectionsDraft, rerender);
|
||||
|
||||
const wireDisclosure = (): void => {
|
||||
wireAttachmentsDisclosure(app, {
|
||||
itemId: existing?.id ?? '',
|
||||
attachments: attachmentsDraft,
|
||||
mode: 'edit',
|
||||
onChange: (next) => {
|
||||
attachmentsDraft = next;
|
||||
const disc = app.querySelector('.attachments-disclosure');
|
||||
if (disc) {
|
||||
disc.outerHTML = renderAttachmentsDisclosure({
|
||||
itemId: existing?.id ?? '',
|
||||
attachments: attachmentsDraft,
|
||||
mode: 'edit',
|
||||
});
|
||||
wireDisclosure();
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
wireDisclosure();
|
||||
if (isInTab()) {
|
||||
const wireDisclosure = (): void => {
|
||||
wireAttachmentsDisclosure(app, {
|
||||
itemId: existing?.id ?? '',
|
||||
attachments: attachmentsDraft,
|
||||
mode: 'edit',
|
||||
onChange: (next) => {
|
||||
attachmentsDraft = next;
|
||||
const disc = app.querySelector('.attachments-disclosure');
|
||||
if (disc) {
|
||||
disc.outerHTML = renderAttachmentsDisclosure({
|
||||
itemId: existing?.id ?? '',
|
||||
attachments: attachmentsDraft,
|
||||
mode: 'edit',
|
||||
});
|
||||
wireDisclosure();
|
||||
}
|
||||
},
|
||||
});
|
||||
};
|
||||
wireDisclosure();
|
||||
}
|
||||
|
||||
document.getElementById('cancel-btn')?.addEventListener('click', () => {
|
||||
setState({ error: null });
|
||||
|
||||
@@ -30,6 +30,10 @@ export function escapeHtml(str: string): string {
|
||||
|
||||
// --- Pop out to tab ---
|
||||
|
||||
export function isInTab(): boolean {
|
||||
return window.location.search.length > 0;
|
||||
}
|
||||
|
||||
export function popOutToTab(): void {
|
||||
const state = getState();
|
||||
const params = new URLSearchParams();
|
||||
|
||||
Reference in New Issue
Block a user