Initial commit

This commit is contained in:
pdmarf
2026-04-10 19:51:21 +01:00
commit bc383e4fd8
22 changed files with 2190 additions and 0 deletions

View File

@@ -0,0 +1,255 @@
<!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; }
#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; }
</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>User ID</label>
<input type="text" id="userId" placeholder="Your Notion user UUID">
</div>
<p class="hint">Shared across all buttons. Enter 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>
<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;
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.trim(),
};
$PI.setGlobalSettings(creds);
setCredStatus("Credentials saved.", "ok");
}
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);
["notionToken", "userId"].forEach(function(id) {
document.getElementById(id).addEventListener("input", scheduleCredSave);
});
});
$PI.onDidReceiveGlobalSettings(function(jsn) {
var s = jsn.payload.settings || {};
document.getElementById("notionToken").value = s.notionToken || "";
document.getElementById("userId").value = s.userId || "";
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 === "projects") {
if (payload.error) {
setStatus(payload.error, "error");
} else {
populateProjects(payload.data);
}
}
});
</script>
</body>
</html>