plugin infrastruktur
This commit is contained in:
1 parent
1545b90f39
commit
3f4ba3c0f4
35 files changed
+1303
-195
No files matched your search
+50
-46
@@ -301,6 +301,33 @@ if (str_starts_with($route, '/api/')) {
|
||||
$card->execute(['id' => $m[1]]); echo json_encode($card->fetch()); exit;
|
||||
}
|
||||
|
||||
// Plugins
|
||||
if ($route === '/api/plugins' && $method === 'GET') {
|
||||
echo json_encode(PluginManager::instance()->pluginsForApi()); exit;
|
||||
}
|
||||
|
||||
if ($route === '/api/plugins/toggle' && $method === 'POST') {
|
||||
$input = json_decode(file_get_contents('php://input'), true);
|
||||
$name = trim((string)($input['name'] ?? ''));
|
||||
$enabled = !empty($input['enabled']);
|
||||
if ($name === '') { http_response_code(400); echo json_encode(['error' => 'name required']); exit; }
|
||||
$pm = PluginManager::instance();
|
||||
$known = array_keys($pm->plugins());
|
||||
if (!in_array($name, $known, true)) {
|
||||
http_response_code(404); echo json_encode(['error' => 'plugin not found']); exit;
|
||||
}
|
||||
$disabled = $pm->disabledList();
|
||||
if ($enabled) {
|
||||
$disabled = array_values(array_diff($disabled, [$name]));
|
||||
} else if (!in_array($name, $disabled, true)) {
|
||||
$disabled[] = $name;
|
||||
}
|
||||
if (!$pm->setDisabled($disabled)) {
|
||||
http_response_code(500); echo json_encode(['error' => 'failed to write state']); exit;
|
||||
}
|
||||
echo json_encode(['ok' => true, 'plugins' => $pm->pluginsForApi()]); exit;
|
||||
}
|
||||
|
||||
// Buckets
|
||||
if ($route === '/api/buckets' && $method === 'GET') {
|
||||
echo json_encode($db->query('SELECT * FROM buckets ORDER BY position ASC, created_at ASC')->fetchAll()); exit;
|
||||
@@ -1012,6 +1039,7 @@ if (str_starts_with($route, '/api/')) {
|
||||
<script src="js/vendor/marked.min.js"></script>
|
||||
<script src="js/vendor/purify.min.js"></script>
|
||||
<link rel="stylesheet" href="css/style.css?v=<?= time() ?>">
|
||||
<?= PluginManager::instance()->cssLinks() ?>
|
||||
<script>
|
||||
window.themes = {
|
||||
slate: { label:'Slate', vars:{ '--t-bg':'#1e293b','--t-surface':'#273349','--t-raised':'#2e3d56','--t-border':'#384a64','--t-dim':'#64748b','--t-muted':'#94a3b8','--t-text':'#cbd5e1','--t-bright':'#f1f5f9','--t-accent':'#38bdf8','--t-accent2':'#818cf8','--t-danger':'#fb7185','--t-scanline':'rgba(0,0,0,0.04)','--t-overlay':'rgba(15,23,42,0.75)','--t-font-pixel':'"Silkscreen", cursive','--t-cover-gradient':'linear-gradient(180deg, rgba(15,23,42,0.22) 0%, rgba(15,23,42,0.7) 62%, rgba(15,23,42,0.86) 100%)' }},
|
||||
@@ -1350,6 +1378,7 @@ if (str_starts_with($route, '/api/')) {
|
||||
<button class="settings-tab" data-tab="relay"><i class="ph ph-broadcast text-sm"></i><span class="font-pixel text-[10px]">relay</span></button>
|
||||
<button class="settings-tab" data-tab="behavior"><i class="ph ph-sliders text-sm"></i><span class="font-pixel text-[10px]">behavior</span></button>
|
||||
<button class="settings-tab" data-tab="notifications"><i class="ph ph-bell text-sm"></i><span class="font-pixel text-[10px]">notifications</span></button>
|
||||
<button class="settings-tab" data-tab="plugins"><i class="ph ph-puzzle-piece text-sm"></i><span class="font-pixel text-[10px]">plugins</span></button>
|
||||
</div>
|
||||
<div class="flex-1 flex flex-col min-h-0">
|
||||
<div class="flex items-center gap-3 px-5 py-3 border-b border-t-border shrink-0">
|
||||
@@ -1521,6 +1550,18 @@ if (str_starts_with($route, '/api/')) {
|
||||
<div id="notif-stream" class="max-h-64 overflow-y-auto space-y-1"></div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="settings-panel-plugins" class="hidden space-y-4">
|
||||
<div>
|
||||
<div class="font-pixel text-[9px] uppercase tracking-wider text-t-dim mb-2"><i class="ph ph-puzzle-piece text-xs"></i> installed plugins</div>
|
||||
<p class="text-[10px] text-t-dim leading-relaxed mb-3">Plugins extend hyprcrd with optional features. Disable a plugin to skip loading its assets entirely. Toggling requires a page reload to take effect.</p>
|
||||
<div id="plugins-list" class="space-y-2"></div>
|
||||
<div id="plugins-reload-notice" class="hidden mt-3 flex items-center gap-2 px-3 py-2 border border-t-accent bg-t-accent/10 text-xs text-t-accent">
|
||||
<i class="ph ph-warning text-sm"></i>
|
||||
<span class="flex-1 font-pixel text-[10px]">reload required to apply changes</span>
|
||||
<button id="plugins-reload-btn" class="font-pixel text-[10px] underline hover:text-t-bright">reload now</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div id="settings-panel-account" class="hidden space-y-4">
|
||||
<div>
|
||||
<div class="font-pixel text-[9px] uppercase tracking-wider text-t-dim mb-2"><i class="ph ph-key text-xs"></i> vault key</div>
|
||||
@@ -1826,7 +1867,7 @@ if (str_starts_with($route, '/api/')) {
|
||||
<button data-md="hr" title="Divider" class="toolbar-btn"><i class="ph ph-minus text-sm"></i></button>
|
||||
<div class="w-px h-4 bg-t-border mx-1"></div>
|
||||
<button data-md="extract" title="Extract selection to new card" class="toolbar-btn"><i class="ph ph-scissors text-sm"></i></button>
|
||||
<button data-md="asciiart" title="ASCII Canvas" class="toolbar-btn"><i class="ph ph-grid-four text-sm"></i></button>
|
||||
<?= PluginManager::instance()->slot('md-toolbar') ?>
|
||||
<div class="flex-1"></div>
|
||||
<button id="toggle-lines" class="toolbar-btn flex items-center gap-1.5 px-2" title="line numbers"><i class="ph ph-hash text-sm"></i></button>
|
||||
<button id="toggle-preview" class="toolbar-btn flex items-center gap-1.5 px-2"><i class="ph ph-eye text-sm"></i><span class="font-pixel text-[9px]">preview</span></button>
|
||||
@@ -1843,51 +1884,14 @@ if (str_starts_with($route, '/api/')) {
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- ASCII Canvas Overlay -->
|
||||
<div class="overlay fixed inset-0" id="ascii-canvas-overlay" style="z-index:80;">
|
||||
<div class="ascii-canvas-modal bg-t-surface border border-t-border rounded-lg shadow-xl flex flex-col" style="width:90vw;max-width:960px;height:85vh;max-height:700px;margin:auto;position:absolute;inset:0;">
|
||||
<!-- Toolbar -->
|
||||
<div class="flex items-center gap-1 px-3 py-2 border-b border-t-border shrink-0 flex-wrap" id="ascii-canvas-toolbar">
|
||||
<button data-tool="brush" class="toolbar-btn active" title="Brush (B)"><i class="ph ph-pencil-simple text-sm"></i></button>
|
||||
<button data-tool="eraser" class="toolbar-btn" title="Eraser (E)"><i class="ph ph-eraser text-sm"></i></button>
|
||||
<button data-tool="line" class="toolbar-btn" title="Line (L)"><i class="ph ph-line-segment text-sm"></i></button>
|
||||
<button data-tool="rect" class="toolbar-btn" title="Rectangle (R)"><i class="ph ph-rectangle text-sm"></i></button>
|
||||
<button data-tool="circle" class="toolbar-btn" title="Ellipse (O)"><i class="ph ph-circle text-sm"></i></button>
|
||||
<button data-tool="fill" class="toolbar-btn" title="Fill (F)"><i class="ph ph-paint-bucket text-sm"></i></button>
|
||||
<button data-tool="text" class="toolbar-btn" title="Text (T)"><i class="ph ph-text-t text-sm"></i></button>
|
||||
<div class="w-px h-4 bg-t-border mx-1"></div>
|
||||
<label class="flex items-center gap-1 text-t-muted text-xs">
|
||||
<span>Char:</span>
|
||||
<input id="ascii-char-input" type="text" value="#" maxlength="1" class="w-6 text-center bg-t-bg border border-t-border rounded text-t-text text-sm font-mono px-0 py-0">
|
||||
</label>
|
||||
<div class="w-px h-4 bg-t-border mx-1"></div>
|
||||
<label class="flex items-center gap-1 text-t-muted text-xs">
|
||||
<span>W:</span>
|
||||
<input id="ascii-canvas-width" type="number" value="40" min="5" max="200" class="w-12 bg-t-bg border border-t-border rounded text-t-text text-xs text-center px-1 py-0">
|
||||
</label>
|
||||
<label class="flex items-center gap-1 text-t-muted text-xs">
|
||||
<span>H:</span>
|
||||
<input id="ascii-canvas-height" type="number" value="20" min="3" max="100" class="w-12 bg-t-bg border border-t-border rounded text-t-text text-xs text-center px-1 py-0">
|
||||
</label>
|
||||
<button id="ascii-canvas-resize" class="toolbar-btn text-xs" title="Resize"><i class="ph ph-arrows-out text-sm"></i></button>
|
||||
<div class="w-px h-4 bg-t-border mx-1"></div>
|
||||
<button id="ascii-canvas-clear" class="toolbar-btn text-xs" title="Clear"><i class="ph ph-trash text-sm"></i></button>
|
||||
<div class="flex-1"></div>
|
||||
<span id="ascii-canvas-coords" class="text-t-dim text-xs font-mono">1:1</span>
|
||||
</div>
|
||||
<!-- Char palette -->
|
||||
<div id="ascii-char-palette" class="flex items-center gap-0.5 px-3 py-1.5 border-b border-t-border shrink-0 flex-wrap overflow-x-auto"></div>
|
||||
<!-- Grid -->
|
||||
<div class="flex-1 min-h-0 overflow-auto bg-t-bg p-2">
|
||||
<pre id="ascii-canvas-grid" class="ascii-grid font-mono text-sm text-t-text select-none cursor-crosshair" style="line-height:1.35;letter-spacing:0;margin:0;"></pre>
|
||||
</div>
|
||||
<!-- Footer -->
|
||||
<div class="flex items-center justify-end gap-2 px-3 py-2 border-t border-t-border shrink-0">
|
||||
<button id="ascii-canvas-cancel" class="toolbar-btn text-xs px-3 py-1">Cancel</button>
|
||||
<button id="ascii-canvas-ok" class="bg-t-accent text-t-bg text-xs font-bold px-4 py-1 rounded hover:opacity-90 cursor-pointer">Insert</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- Plugin HTML fragments (overlays, modals, etc.) -->
|
||||
<?= PluginManager::instance()->htmlFragments() ?>
|
||||
|
||||
<!-- Plugin API: must load BEFORE any ES module so window.hyprcrd exists -->
|
||||
<script src="js/hyprcrd.js?v=<?= time() ?>"></script>
|
||||
|
||||
<!-- Plugin entry points (load before app.js so they self-register first) -->
|
||||
<?= PluginManager::instance()->scriptTags() ?>
|
||||
|
||||
<script type="module" src="js/app.js?v=<?= time() ?>"></script>
|
||||
</body>
|
||||
|
||||
Reference in new issue
Block a user