Files
stream_deck_notion_timer/com.pdma.notion-timer.sdPlugin/ui/property-inspector.html
pdmarf c3add8da27 v1.0.28: fix name dropdown not restoring when switching buttons
onDidReceiveGlobalSettings fired before the users list was populated,
so the saved userId couldn't be selected. Now stored in globalUserId
variable and applied when populateUsers runs, regardless of order.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-24 08:50:39 +01:00

323 lines
9.5 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
font-size: 13px;
color: #ccc;
background: transparent;
padding: 8px;
}
.section-title {
font-size: 11px;
font-weight: 600;
text-transform: uppercase;
letter-spacing: 0.05em;
color: #888;
margin: 14px 0 8px;
cursor: pointer;
user-select: none;
display: flex;
align-items: center;
gap: 4px;
}
.section-title:first-child { margin-top: 0; }
.section-title .arrow { font-size: 9px; }
.collapsible { overflow: hidden; }
.collapsible.collapsed { display: none; }
.row {
display: flex;
align-items: center;
gap: 8px;
margin-bottom: 8px;
}
label {
width: 90px;
flex-shrink: 0;
color: #999;
font-size: 12px;
}
select, input[type="text"], input[type="password"] {
flex: 1;
background: #1a1a1a;
border: 1px solid #444;
border-radius: 4px;
color: #eee;
padding: 5px 8px;
font-size: 12px;
outline: none;
}
.hint {
font-size: 11px;
color: #555;
margin-top: -4px;
margin-bottom: 8px;
padding-left: 98px;
line-height: 1.4;
}
.divider {
border: none;
border-top: 1px solid #333;
margin: 10px 0;
}
#statusText {
font-size: 11px;
color: #888;
text-align: center;
padding-top: 4px;
min-height: 16px;
}
#statusText.running { color: #4caf50; }
#statusText.error { color: #e57373; }
#versionText {
font-size: 12px;
color: #fff;
text-align: center;
padding-top: 8px;
}
#credStatus {
font-size: 11px;
color: #888;
text-align: center;
padding-top: 2px;
margin-bottom: 6px;
min-height: 14px;
}
#credStatus.ok { color: #4caf50; }
#credStatus.error { color: #e57373; }
#updateBtn {
display: block;
width: 100%;
margin-top: 10px;
padding: 6px;
background: #2a2a2a;
border: 1px solid #444;
border-radius: 4px;
color: #ccc;
font-size: 12px;
cursor: pointer;
}
#updateBtn:hover { background: #333; }
#updateStatus {
font-size: 11px;
color: #888;
text-align: center;
margin-top: 4px;
min-height: 14px;
}
</style>
</head>
<body>
<!-- Credentials (collapsed by default once configured) -->
<p class="section-title" id="credToggle">
<span class="arrow" id="credArrow"></span> Notion Credentials
</p>
<div class="collapsible collapsed" id="credSection">
<div class="row">
<label>API Token</label>
<input type="password" id="notionToken" placeholder="ntn_…">
</div>
<div class="row">
<label>Your Name</label>
<select id="userId">
<option value="">— Select your name —</option>
</select>
</div>
<p class="hint">Shared across all buttons. Select once per device.</p>
<p id="credStatus"></p>
<hr class="divider">
</div>
<!-- Per-button settings -->
<p class="section-title" style="cursor:default;">
<span class="arrow" style="visibility:hidden;"></span> Button
</p>
<div class="row">
<label>Project</label>
<select id="projectSelect">
<option value="">Loading projects…</option>
</select>
</div>
<p id="statusText"></p>
<p id="versionText"></p>
<button id="updateBtn">Check for Updates</button>
<p id="updateStatus"></p>
<script src="libs/constants.js"></script>
<script src="libs/prototypes.js"></script>
<script src="libs/timers.js"></script>
<script src="libs/utils.js"></script>
<script src="libs/events.js"></script>
<script src="libs/api.js"></script>
<script src="libs/property-inspector.js"></script>
<script>
var ACTION_UUID = "com.pdma.notion-timer.toggle";
var currentSettings = {};
var credSaveTimer = null;
var credConfigured = false;
var globalUserId = "";
function setStatus(msg, cls) {
var el = document.getElementById("statusText");
el.textContent = msg;
el.className = cls || "";
}
function setCredStatus(msg, cls) {
var el = document.getElementById("credStatus");
el.textContent = msg;
el.className = cls || "";
}
// Collapsible credentials section
document.getElementById("credToggle").addEventListener("click", function() {
var section = document.getElementById("credSection");
var arrow = document.getElementById("credArrow");
if (section.classList.contains("collapsed")) {
section.classList.remove("collapsed");
arrow.textContent = "▼";
} else {
section.classList.add("collapsed");
arrow.textContent = "▶";
}
});
function saveCredentials() {
var creds = {
notionToken: document.getElementById("notionToken").value.trim(),
userId: document.getElementById("userId").value,
};
$PI.setGlobalSettings(creds);
setCredStatus("Credentials saved.", "ok");
if (creds.notionToken) {
setStatus("Loading…", "");
$PI.sendToPlugin({ event: "refreshProjects", token: creds.notionToken });
}
}
function populateUsers(users, savedUserId) {
var sel = document.getElementById("userId");
var current = savedUserId || sel.value;
sel.innerHTML = '<option value="">— Select your name —</option>';
users.forEach(function(u) {
var opt = document.createElement("option");
opt.value = u.id;
opt.textContent = u.name;
if (u.id === current) opt.selected = true;
sel.appendChild(opt);
});
}
function scheduleCredSave() {
clearTimeout(credSaveTimer);
credSaveTimer = setTimeout(saveCredentials, 600);
}
function save() {
var sel = document.getElementById("projectSelect");
var opt = sel.options[sel.selectedIndex];
currentSettings.projectId = sel.value;
currentSettings.projectName = sel.value ? opt.textContent.trim() : "";
$PI.setSettings(currentSettings);
$PI.sendToPlugin({ event: "saveSettings", settings: currentSettings });
setStatus(currentSettings.projectName ? "Saved: " + currentSettings.projectName : "", "");
}
function populateProjects(projects) {
var sel = document.getElementById("projectSelect");
sel.innerHTML = '<option value="">— Select project —</option>';
projects.forEach(function(p) {
var opt = document.createElement("option");
opt.value = p.id;
opt.textContent = p.name;
if (currentSettings.projectId === p.id) opt.selected = true;
sel.appendChild(opt);
});
if (!currentSettings.projectId) {
setStatus("Select a project to get started.", "");
} else if (currentSettings.activeEntryId) {
setStatus("⏱ Timer running", "running");
} else {
setStatus("", "");
}
}
$PI.onConnected(function(jsn) {
currentSettings = jsn.actionInfo.payload.settings || {};
$PI.getSettings();
$PI.getGlobalSettings();
document.getElementById("projectSelect").addEventListener("change", save);
document.getElementById("notionToken").addEventListener("input", scheduleCredSave);
document.getElementById("userId").addEventListener("change", saveCredentials);
document.getElementById("updateBtn").addEventListener("click", function() {
document.getElementById("updateStatus").textContent = "";
$PI.sendToPlugin({ event: "checkForUpdates" });
});
});
$PI.onDidReceiveGlobalSettings(function(jsn) {
var s = jsn.payload.settings || {};
document.getElementById("notionToken").value = s.notionToken || "";
globalUserId = s.userId || "";
if (globalUserId) {
var sel = document.getElementById("userId");
if (sel.querySelector('option[value="' + globalUserId + '"]')) {
sel.value = globalUserId;
}
}
credConfigured = !!(s.notionToken && s.userId);
// Auto-collapse if already configured, expand if not
var section = document.getElementById("credSection");
var arrow = document.getElementById("credArrow");
if (credConfigured) {
section.classList.add("collapsed");
arrow.textContent = "▶";
} else {
section.classList.remove("collapsed");
arrow.textContent = "▼";
}
});
$PI.onDidReceiveSettings(ACTION_UUID, function(jsn) {
currentSettings = jsn.payload.settings || {};
var sel = document.getElementById("projectSelect");
if (currentSettings.projectId && sel.options.length > 1) {
sel.value = currentSettings.projectId;
}
if (currentSettings.activeEntryId) {
setStatus("⏱ Timer running", "running");
}
});
$PI.onSendToPropertyInspector(ACTION_UUID, function(jsn) {
var payload = jsn.payload;
if (payload.event === "updateStatus") {
document.getElementById("updateStatus").textContent = payload.message;
}
if (payload.event === "projects") {
if (payload.version) {
document.getElementById("versionText").textContent = "v" + payload.version;
}
if (payload.users) {
populateUsers(payload.users, globalUserId);
}
if (payload.error) {
setStatus(payload.error, "error");
} else {
populateProjects(payload.data);
}
}
});
</script>
</body>
</html>