repositories / dotfiles
dotfiles
bugabingas dorkfiles
owned by admin
quickshell/nuguland/Vault.qml
Rawpragma ComponentBehavior: Bound
import QtQuick
import Quickshell
import Quickshell.Io
import "vault.js" as VaultData
Item {
id: root
required property Clipboard clipboardService
property string status: "needs-setup"
property string message: ""
property var profile: ({
"name": "personal",
"serverUrl": "",
"account": "",
"unlockTimeoutSeconds": 900,
"clipboardTimeoutSeconds": 30
})
property var cache: ({
"available": false,
"ageSeconds": -1,
"stale": false
})
property var items: []
property var detail: ({})
property var challenge: ({})
property int secondFactorProvider: -1
property string secondFactorDraft: ""
readonly property bool verificationPending: status === "second-factor" && message === "verifying"
property string query: ""
property string selectedId: ""
property string browserOrigin: ""
property string profileKey: ""
property real resultScrollPosition: 0
property real detailScrollPosition: 0
property var expandedSections: ({})
property var revealedFields: ({})
property var faviconPaths: ({})
property string feedback: ""
property string repromptError: ""
property bool copyPending: false
property double currentCopyId: 0
property int lockGeneration: 0
property bool panelOpen: false
property bool lockPending: false
property bool restoreSyncPending: false
readonly property var visibleItems: VaultData.filterItems(items, query, browserOrigin)
readonly property var groupedItems: VaultData.groups(visibleItems)
readonly property bool unlocked: status === "unlocked" || status === "syncing" || status === "sync-failure" || status === "stale-cache"
readonly property string executable: Quickshell.shellPath("vault-native/run.sh")
readonly property string configPath: Quickshell.statePath("vault.json")
property bool configLoaded: false
property bool setupEditing: false
property bool setupSaving: false
property string setupUrlDraft: ""
property string setupAccountDraft: ""
property string setupError: ""
property var pendingProfile: null
property bool persistProfile: false
property double configurationId: 0
property bool restartPending: false
property bool connectionTesting: false
property string connectionResult: ""
readonly property bool connectionBusy: connectionTesting || connectionProcess.running
function send(request) {
if (!vaultProcess.running) {
boundaryFailed("native vault boundary unavailable");
return;
}
vaultProcess.write(JSON.stringify(request) + "\n");
}
function boundaryFailed(reason) {
status = "unavailable-vault";
message = reason;
lockPending = false;
pendingProfile = null;
restartPending = false;
if (setupSaving) {
setupEditing = true;
setupError = reason;
}
setupSaving = false;
persistProfile = false;
clearRetainedState();
}
function beginSetup() {
setupUrlDraft = String(profile.serverUrl || "");
setupAccountDraft = String(profile.account || "");
setupError = "";
setupEditing = true;
lock();
}
function cancelSetup() {
stopConnectionTest();
setupEditing = false;
setupError = "";
setupUrlDraft = String(profile.serverUrl || "");
setupAccountDraft = String(profile.account || "");
}
function configureProfile(value, persist) {
pendingProfile = value;
persistProfile = persist;
setupSaving = true;
setupError = "";
configurationId++;
lockPending = true;
status = "locked";
message = "";
browserOrigin = "";
clearRetainedState();
// EOF locks and reaps the old boundary before starting the current executable.
// Quickshell otherwise retains the old process across QML reloads and builds.
restartPending = vaultProcess.running;
if (restartPending)
vaultProcess.stdinEnabled = false;
else {
vaultProcess.stdinEnabled = true;
vaultProcess.running = true;
}
}
function boundaryStarted() {
if (setupSaving && pendingProfile !== null && !restartPending)
send({
"op": "configure-lock",
"id": configurationId
});
}
function boundaryExited() {
clipboardService.resumeAfterVaultCopy();
if (restartPending) {
restartPending = false;
vaultProcess.stdinEnabled = true;
vaultProcess.running = true;
} else
boundaryFailed("native vault boundary stopped");
}
function setupTimedOut() {
boundaryFailed("vault setup timed out; retry saving");
vaultProcess.stdinEnabled = false;
}
function loadConfig() {
if (configLoaded)
return;
configLoaded = true;
let value;
try {
value = JSON.parse(cfgFile.text());
if (!value || typeof value !== "object" || Array.isArray(value))
throw new Error("invalid profile");
} catch (error) {
setupError = "vault settings could not be read";
setupEditing = true;
return;
}
configureProfile(value, false);
}
function draftProfile() {
return {
"name": String(profile.name || "personal"),
"serverUrl": setupUrlDraft.trim(),
"account": setupAccountDraft.trim(),
"unlockTimeoutSeconds": Number(profile.unlockTimeoutSeconds || 900),
"clipboardTimeoutSeconds": Number(profile.clipboardTimeoutSeconds || 30)
};
}
function saveSetup() {
if (setupSaving || lockPending || connectionBusy)
return;
configureProfile(draftProfile(), true);
}
function testConnection() {
if (setupSaving || lockPending || connectionBusy)
return;
setupError = "";
connectionResult = "";
connectionTesting = true;
connectionProcess.stdinEnabled = true;
connectionProcess.running = true;
}
function connectionStarted() {
if (connectionTesting)
connectionProcess.write(JSON.stringify({
"op": "test-connection",
"profile": draftProfile()
}) + "\n");
}
function connectionExited() {
if (connectionTesting && !connectionResult)
connectionResult = "connection test stopped";
connectionTesting = false;
}
function stopConnectionTest() {
connectionTesting = false;
connectionResult = "";
connectionProcess.stdinEnabled = false;
}
function connectionTimedOut() {
connectionResult = "connection test timed out";
connectionTesting = false;
connectionProcess.stdinEnabled = false;
}
function handleConnectionLine(line) {
if (!connectionTesting)
return;
let event;
try {
event = JSON.parse(line);
} catch (error) {
connectionResult = "invalid connection test response";
connectionProcess.stdinEnabled = false;
return;
}
if (event.event === "connection-result")
connectionResult = String(event.message || "connection test failed");
else if (event.event === "protocol-error")
connectionResult = "native vault rejected connection test";
else if (event.event === "state" && event.status === "unavailable-vault")
connectionResult = String(event.message || "connection test unavailable");
else
return;
connectionProcess.stdinEnabled = false;
}
function unlock(password) {
const value = String(password || "");
if (value.length === 0 || lockPending || setupSaving || setupEditing || !profile.serverUrl || !profile.account)
return;
status = "loading";
send({
"op": "unlock",
"password": value
});
}
function secondFactorHint() {
if (secondFactorProvider === 1)
return "email code selected; check your inbox";
const provider = (challenge.providers || []).find(value => Number(value.id) === secondFactorProvider);
return provider ? String(provider.label) + " selected; enter its verification code" : "choose a verification method";
}
function selectSecondFactor(provider) {
const value = Number(provider);
if (verificationPending || !(challenge.providers || []).some(candidate => Number(candidate.id) === value && !!candidate.codeSupported))
return;
if (secondFactorProvider !== value)
secondFactorDraft = "";
secondFactorProvider = value;
message = "";
}
function submitSecondFactor(token) {
const value = String(token || "");
if (status !== "second-factor" || value.length === 0 || secondFactorProvider < 0 || verificationPending || lockPending)
return;
secondFactorDraft = "";
message = "verifying";
send({
"op": "second-factor",
"token": value,
"provider": secondFactorProvider
});
}
function cancelUnlock() {
lock();
}
function reprompt(password) {
const value = String(password || "");
if (selectedId.length === 0 || value.length === 0)
return;
repromptError = "";
send({
"op": "reprompt",
"id": selectedId,
"password": value
});
}
function prepareOpen(appId) {
send({
"op": "context",
"appId": String(appId || "") || null
});
if (unlocked)
sync();
}
function lock(request) {
stopConnectionTest();
if (!request && setupSaving) {
restartPending = false;
pendingProfile = null;
persistProfile = false;
setupSaving = false;
setupEditing = true;
setupError = "configuration interrupted by lock";
}
lockPending = true;
status = "locked";
message = "";
browserOrigin = "";
clearRetainedState();
send(request || {
"op": "lock"
});
}
function sync() {
send({
"op": "sync"
});
}
function select(id) {
const value = String(id || "");
if (value.length === 0 || value === selectedId)
return;
selectedId = value;
detail = ({});
repromptError = "";
send({
"op": "detail",
"id": selectedId
});
}
function moveSelection(delta) {
const next = VaultData.nextSelection(visibleItems, selectedId, Number(delta));
if (next.length > 0)
select(next);
}
function refreshDetail() {
if (unlocked && selectedId.length > 0)
send({
"op": "detail",
"id": selectedId
});
}
function copy(target, slot) {
if (selectedId.length === 0)
return;
send({
"op": "copy",
"id": selectedId,
"target": String(target),
"slot": slot === undefined ? null : Number(slot)
});
}
function open(field) {
if (selectedId.length === 0)
return;
send({
"op": "open",
"id": selectedId,
"field": String(field)
});
}
function setFavorite(favorite) {
if (selectedId.length === 0)
return;
send({
"op": "favorite",
"id": selectedId,
"favorite": !!favorite
});
}
function sectionExpanded(section) {
const key = selectedId + "/" + String(section);
return expandedSections[key] === undefined ? true : !!expandedSections[key];
}
function toggleSection(section) {
const next = Object.assign({}, expandedSections);
const key = selectedId + "/" + String(section);
next[key] = !sectionExpanded(section);
expandedSections = next;
}
function isRevealed(field) {
return !!revealedFields[selectedId + "/" + String(field)];
}
function toggleReveal(field) {
const next = Object.assign({}, revealedFields);
const key = selectedId + "/" + String(field);
next[key] = !next[key];
revealedFields = next;
}
function clearRetainedState() {
lockGeneration++;
repromptError = "";
feedback = "";
copyPending = false;
restoreSyncPending = false;
items = [];
detail = ({});
challenge = ({});
secondFactorProvider = -1;
secondFactorDraft = "";
query = "";
selectedId = "";
resultScrollPosition = 0;
detailScrollPosition = 0;
expandedSections = ({});
revealedFields = ({});
faviconPaths = ({});
}
function applyState(event) {
status = String(event.status || "unavailable-vault");
message = String(event.message || "");
profile = event.profile || profile;
profileKey = String(event.profileKey || "");
cache = event.cache || cache;
challenge = event.challenge || ({});
if (status !== "second-factor")
secondFactorDraft = "";
const providers = challenge.providers || [];
if (providers.length > 0 && !providers.some(provider => Number(provider.id) === secondFactorProvider && !!provider.codeSupported)) {
const supported = providers.find(provider => !!provider.codeSupported);
secondFactorProvider = supported ? Number(supported.id) : -1;
}
browserOrigin = String(event.browserOrigin || "");
items = event.items || [];
for (let index = 0; index < items.length; index++) {
const path = faviconPaths[String(items[index].id || "")];
if (path)
items[index].favicon = path;
}
if (!unlocked) {
if (status === "needs-setup" || status === "locked" || status === "authentication-failure" || status === "unavailable-vault")
clearRetainedState();
return;
}
if (visibleItems.length > 0) {
const retained = visibleItems.some(item => {
return item.id === selectedId;
});
if (retained)
refreshDetail();
else
select(visibleItems[0].id);
} else {
selectedId = "";
detail = ({});
}
}
function handleLine(line) {
let event;
try {
event = JSON.parse(String(line || ""));
} catch (error) {
boundaryFailed("invalid native vault response");
return;
}
if (restartPending && event.event !== "copy-ended")
return;
if (setupSaving && (event.event === "protocol-error" || event.event === "configure-failed")) {
boundaryFailed(String(event.message || "native vault rejected settings; retry saving"));
return;
}
if (lockPending) {
if (event.event === "state" && (event.status === "needs-setup" || event.status === "locked" || event.status === "unavailable-vault"))
lockPending = false;
else if (event.event !== "copy-ended" && event.event !== "configure-ready")
return;
}
switch (String(event.event || "")) {
case "state":
applyState(event);
if (event.syncNeeded && !restoreSyncPending) {
restoreSyncPending = true;
sync();
} else if (!event.syncNeeded) {
restoreSyncPending = false;
}
if (status === "unavailable-vault" && setupSaving)
boundaryFailed(message || "native vault boundary unavailable");
if (setupSaving && pendingProfile === null && (status === "locked" || status === "needs-setup")) {
setupSaving = false;
persistProfile = false;
setupEditing = true;
setupError = "configuration interrupted by lock";
}
break;
case "configure-ready":
if (pendingProfile !== null && event.id === configurationId) {
lockPending = false;
const value = pendingProfile;
pendingProfile = null;
send({
"op": "configure",
"profile": value
});
}
break;
case "configured":
if (!setupSaving)
break;
profile = event.profile;
setupUrlDraft = String(profile.serverUrl || "");
setupAccountDraft = String(profile.account || "");
setupEditing = false;
setupSaving = false;
if (persistProfile)
cfgFile.setText(JSON.stringify(profile, null, 2));
persistProfile = false;
break;
case "detail":
if (String(event.id || "") === selectedId) {
// TOTP ticks must not recreate field delegates and discard keyboard focus or presses.
if (detail.id === event.id && JSON.stringify(detail.fields) === JSON.stringify(event.fields))
event.fields = detail.fields;
detail = event;
}
break;
case "favicon":
{
if (!unlocked || !profileKey || event.profileKey !== profileKey)
break;
const id = String(event.id || "");
const path = String(event.path || "");
faviconPaths[id] = path;
const updated = items.slice();
const index = updated.findIndex(item => String(item.id || "") === id);
if (index >= 0) {
updated[index] = Object.assign({}, updated[index], {
"favicon": path
});
items = updated;
}
break;
}
case "reprompt-succeeded":
repromptError = "";
feedback = "item access granted";
feedbackTimer.restart();
break;
case "reprompt-failed":
repromptError = "master password incorrect";
break;
case "copy-ready":
copyPending = true;
currentCopyId = Number(event.id || 0);
clipboardService.pauseForVaultCopy(Number(profile.clipboardTimeoutSeconds || 30));
break;
case "copy-ended":
if (Number(event.id || 0) === currentCopyId) {
copyPending = false;
currentCopyId = 0;
clipboardService.resumeAfterVaultCopy();
}
break;
case "copied":
feedback = String(event.field || "field") + " copied";
feedbackTimer.restart();
break;
case "copy-failed":
feedback = "copy failed";
feedbackTimer.restart();
break;
case "opened":
feedback = "url opened";
feedbackTimer.restart();
break;
case "action-failed":
feedback = String(event.action || "action") + " failed";
feedbackTimer.restart();
break;
}
}
function panelClosed() {
stopConnectionTest();
}
function stateJson() {
return JSON.stringify({
"status": status,
"profile": profile,
"cache": cache,
"itemCount": items.length,
"selectedId": selectedId,
"query": query,
"feedback": feedback
});
}
onSetupUrlDraftChanged: connectionResult = ""
onSetupAccountDraftChanged: connectionResult = ""
onPanelOpenChanged: if (!panelOpen)
panelClosed()
Component.onCompleted: send({
"op": "hello"
})
Component.onDestruction: {
connectionProcess.stdinEnabled = false;
clipboardService.resumeAfterVaultCopy();
if (vaultProcess.running)
vaultProcess.write("{\"op\":\"lock\"}\n");
}
Connections {
target: root.clipboardService
function onVaultCapturePaused() {
if (!root.copyPending)
return;
root.copyPending = false;
root.send({
"op": "commit-copy"
});
}
}
Timer {
interval: 1000
repeat: true
running: root.unlocked && !!root.detail.totp
onTriggered: root.refreshDetail()
}
Timer {
interval: 5000
running: root.setupSaving
onTriggered: root.setupTimedOut()
}
Timer {
id: feedbackTimer
interval: 1800
onTriggered: root.feedback = ""
}
FileView {
id: cfgFile
path: root.configPath
onLoaded: root.loadConfig()
onLoadFailed: error => {
root.configLoaded = true;
root.setupEditing = true;
if (error !== FileViewError.FileNotFound)
root.setupError = "vault settings could not be read";
}
onSaveFailed: {
root.setupEditing = true;
root.setupError = "vault settings could not be saved";
}
}
Connections {
target: vaultProcess
function onExited(exitCode) {
root.boundaryExited();
}
}
Timer {
interval: 5000
running: root.connectionTesting
onTriggered: root.connectionTimedOut()
}
Connections {
target: connectionProcess
function onExited(exitCode) {
root.connectionExited();
}
}
Process {
id: connectionProcess
command: [root.executable]
workingDirectory: Quickshell.shellDir
stdinEnabled: true
onStarted: root.connectionStarted()
stdout: SplitParser {
onRead: data => root.handleConnectionLine(data)
}
}
Process {
id: vaultProcess
command: [root.executable]
workingDirectory: Quickshell.shellDir
stdinEnabled: true
running: true
onStarted: root.boundaryStarted()
stdout: SplitParser {
onRead: data => root.handleLine(data)
}
stderr: StdioCollector {
waitForEnd: true
onStreamFinished: if (this.text.length > 0)
root.message = "native vault boundary failed"
}
}
}