/** * Git Sync — enc-password custom field for admin-next. * * The plugin's git-sync/data endpoint never returns the stored password to * the client (it's encrypted at rest and only the boolean `password_stored` * flag is sent back). The field renders a normal password input whose * placeholder reflects whether a password is on file: * * - empty + nothing stored → "Your Git Password or Token" * - empty + stored encrypted → "Your password is securely stored." * - empty + stored unencrypted → "Your password is stored but not encrypted." * * Submitting an empty value tells the server "keep what's already there"; * any non-empty value replaces and re-encrypts. */ const TAG = window.__GRAV_FIELD_TAG; class EncPasswordField extends HTMLElement { constructor() { super(); this.attachShadow({ mode: 'open' }); this._field = null; this._value = ''; this._show = false; this._stored = false; this._encrypted = false; } set field(v) { this._field = v || {}; // Server annotates the resolved blueprint with the current // password storage state (see onApiBlueprintResolved in git-sync.php). this._stored = !!(this._field.password_stored); this._encrypted = !!(this._field.password_encrypted); this._render(); } get field() { return this._field; } set value(v) { const next = (v == null) ? '' : String(v); if (next !== this._value) { this._value = next; // Re-render only if we haven't already mounted, to avoid stomping // on the user's in-flight typing. const input = this.shadowRoot.querySelector('input'); if (!input) this._render(); } } get value() { return this._value; } connectedCallback() { this._render(); } _placeholder() { if (this._value) return this._field.placeholder || ''; if (this._stored && this._encrypted) return 'Your password is securely stored.'; if (this._stored && !this._encrypted) return 'Your password is stored but not encrypted.'; return this._field.placeholder || 'Your Git Password or Token'; } _onInput(e) { this._value = e.target.value; this.dispatchEvent(new CustomEvent('change', { detail: this._value, bubbles: true, })); } _toggleVisibility() { this._show = !this._show; const input = this.shadowRoot.querySelector('input'); const btn = this.shadowRoot.querySelector('.toggle'); if (input) input.type = this._show ? 'text' : 'password'; if (btn) btn.setAttribute('aria-pressed', String(this._show)); if (btn) btn.innerHTML = this._show ? this._eyeOff() : this._eye(); } _eye() { return ''; } _eyeOff() { return ''; } _render() { const placeholder = this._placeholder(); const value = this._value; const autocomplete = (this._field && this._field.autocomplete) || 'new-password'; this.shadowRoot.innerHTML = `
${this._stored && !this._encrypted ? '
Existing password is stored unencrypted — saving the form will encrypt it.
' : ''} `; const input = this.shadowRoot.querySelector('input'); input.addEventListener('input', (e) => this._onInput(e)); const toggle = this.shadowRoot.querySelector('.toggle'); toggle.addEventListener('click', () => this._toggleVisibility()); } } customElements.define(TAG, EncPasswordField);