|
@@ -5,124 +5,159 @@
|
|
|
<pstm-template>main</pstm-template>
|
|
<pstm-template>main</pstm-template>
|
|
|
</head>
|
|
</head>
|
|
|
<body>
|
|
<body>
|
|
|
- <section>
|
|
|
|
|
- <h2>Saddles</h2>
|
|
|
|
|
|
|
+ <!-- Goal 41 T8 (ux-notes quick win 8): each saddle card is split
|
|
|
|
|
+ into a "now" HEAD (name, status badge, the one-line health
|
|
|
|
|
+ verdict, current work) and an "admin" TAIL (keys, git
|
|
|
|
|
+ identity, prompt note, capabilities, token, refresh/remove)
|
|
|
|
|
+ folding under one "Keys & admin" expander. The three
|
|
|
|
|
+ health fragments (badge / daemon / up X / contacted Y) are
|
|
|
|
|
+ consolidated server-side into one verdict line
|
|
|
|
|
+ (saddle.health_verdict). The fleet summary line above the
|
|
|
|
|
+ grid surfaces offline/stale/fenced saddles immediately, and
|
|
|
|
|
+ the capability catalogue became its own h2 "Routing tags"
|
|
|
|
|
+ section (recommendation C). -->
|
|
|
|
|
+ <div class="stack">
|
|
|
|
|
+ <div class="cluster cluster-between">
|
|
|
|
|
+ <h2>Saddles</h2>
|
|
|
|
|
+ <button type="button" class="btn-primary" stm-if="saddles != null && saddles.is_admin"
|
|
|
|
|
+ onclick="window.openAppDialog('saddle-register-dialog')">+ Register saddle</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <p class="text-muted">Fleet health and configuration. The dispatcher pairs queued tasks
|
|
|
|
|
+ with idle saddles carrying every tag a task requires.</p>
|
|
|
<p stm-if="saddles == null">Loading…</p>
|
|
<p stm-if="saddles == null">Loading…</p>
|
|
|
|
|
|
|
|
- <div stm-if="saddles != null">
|
|
|
|
|
- <details stm-if="saddles.is_admin">
|
|
|
|
|
- <summary><strong>Register saddle</strong></summary>
|
|
|
|
|
- <form stm-action="saddles.register">
|
|
|
|
|
- <div class="field"><label>Name</label>
|
|
|
|
|
- <input type="text" name="name" required placeholder="saddle-1"></div>
|
|
|
|
|
- <div class="field"><label>Host</label>
|
|
|
|
|
- <input type="text" name="host" required placeholder="localhost:7100"></div>
|
|
|
|
|
- <button type="submit">Register</button>
|
|
|
|
|
- </form>
|
|
|
|
|
- </details>
|
|
|
|
|
|
|
+ <div stm-if="saddles != null" class="stack">
|
|
|
|
|
+
|
|
|
|
|
+ <!-- Fleet health summary (recommendation B): one line when
|
|
|
|
|
+ any saddle is offline/stale/fenced, naming the worst
|
|
|
|
|
+ offenders so the dispatcher risk leads the page. -->
|
|
|
|
|
+ <p stm-if="saddles.fleet_alert.length > 0" class="alert alert-warning"
|
|
|
|
|
+ stm-text="saddles.fleet_alert">fleet alert</p>
|
|
|
|
|
|
|
|
<div class="cards">
|
|
<div class="cards">
|
|
|
<section class="card" stm-for-saddle-in="saddles.saddles">
|
|
<section class="card" stm-for-saddle-in="saddles.saddles">
|
|
|
- <h3 stm-text="saddle.name">name</h3>
|
|
|
|
|
- <p><span stm-attribute.class="'badge badge-' + saddle.status" stm-text="saddle.status">status</span>
|
|
|
|
|
- <span class="text-muted" stm-text="saddle.host">host</span></p>
|
|
|
|
|
- <p stm-if="saddle.current_goal_id > 0">Supervising goal:
|
|
|
|
|
- <a stm-attribute.href="'/goals/' + saddle.current_goal_id"
|
|
|
|
|
- stm-text="saddle.current_goal_title">goal</a>
|
|
|
|
|
- <span class="badge badge-kind-goal">goal</span>
|
|
|
|
|
- <span class="text-muted" stm-if="saddle.allocated_display.length > 0" stm-text="'since ' + saddle.allocated_display">since</span></p>
|
|
|
|
|
- <p stm-if="saddle.current_task_id > 0 && saddle.current_goal_id == 0">Current task:
|
|
|
|
|
- <a stm-attribute.href="'/tasks/' + saddle.current_task_id" stm-text="saddle.current_task_title">task</a>
|
|
|
|
|
- <span class="text-muted" stm-if="saddle.allocated_display.length > 0" stm-text="'since ' + saddle.allocated_display">since</span></p>
|
|
|
|
|
- <p stm-if="saddle.current_task_id == 0" class="text-muted">No current task</p>
|
|
|
|
|
- <p><span stm-attribute.class="'badge badge-health-' + saddle.health"
|
|
|
|
|
- stm-text="saddle.health">healthy</span>
|
|
|
|
|
- <span class="text-muted" stm-if="saddle.daemon_state.length > 0"
|
|
|
|
|
- stm-text="'daemon ' + saddle.daemon_state">daemon</span>
|
|
|
|
|
- <span class="text-muted" stm-if="saddle.daemon_state.length > 0 && saddle.uptime_display.length > 0">· up <span stm-text="saddle.uptime_display">uptime</span></span>
|
|
|
|
|
- <span class="text-muted" stm-text="'contacted ' + saddle.last_contact_display + ' ago'">contacted</span></p>
|
|
|
|
|
- <p stm-if="saddles.is_admin && saddle.token.length > 0" class="text-muted">API token:
|
|
|
|
|
- <code stm-text="saddle.token">token</code></p>
|
|
|
|
|
- <details stm-if="saddles.is_admin && saddle.ssh_public_key.length > 0">
|
|
|
|
|
- <summary>SSH public key</summary>
|
|
|
|
|
- <p class="text-muted">Add this as a <strong>deploy key</strong> on the git server
|
|
|
|
|
- (with write access) so this saddle can clone and push.</p>
|
|
|
|
|
- <pre class="ssh-key"><code stm-text="saddle.ssh_public_key">key</code></pre>
|
|
|
|
|
- <button type="button"
|
|
|
|
|
- stm-attribute.data-clipboard="saddle.ssh_public_key"
|
|
|
|
|
- onclick="navigator.clipboard.writeText(this.getAttribute('data-clipboard')).then(function () { this.textContent = 'Copied'; }.bind(this))">Copy key</button>
|
|
|
|
|
- </details>
|
|
|
|
|
- <p stm-if="saddles.is_admin && saddle.ssh_public_key.length == 0" class="text-muted">No SSH key reported yet —
|
|
|
|
|
- it appears here once the daemon generates one.</p>
|
|
|
|
|
- <details stm-if="saddles.is_admin">
|
|
|
|
|
- <summary>Git identity</summary>
|
|
|
|
|
- <form stm-action="saddle.update_identity" class="cluster">
|
|
|
|
|
- <label>Committed as</label>
|
|
|
|
|
- <input type="text" name="git_name" stm-attribute.value="saddle.git_name"
|
|
|
|
|
- placeholder="saddle-1">
|
|
|
|
|
- <input type="text" name="git_email" stm-attribute.value="saddle.git_email"
|
|
|
|
|
- placeholder="saddle-1@example.net">
|
|
|
|
|
- <button type="submit">Save</button>
|
|
|
|
|
- <span class="text-muted">Empty fields fall back to the daemon's default identity.</span>
|
|
|
|
|
- </form>
|
|
|
|
|
- </details>
|
|
|
|
|
- <details stm-if="saddles.is_admin">
|
|
|
|
|
- <summary>Prompt note</summary>
|
|
|
|
|
- <form stm-action="saddle.update_prompt_note" class="cluster">
|
|
|
|
|
- <label for="prompt-note">Served inside every assignment's system prompt</label>
|
|
|
|
|
- <textarea id="prompt-note" name="prompt_note" rows="3"
|
|
|
|
|
- stm-attribute.placeholder="'e.g. You have root and may install development tooling via DNF'"
|
|
|
|
|
- stm-text="saddle.prompt_note"></textarea>
|
|
|
|
|
- <button type="submit">Save</button>
|
|
|
|
|
- <span class="text-muted">A standing message about THIS machine for its agents; empty removes it (capped at 2000 chars).</span>
|
|
|
|
|
- </form>
|
|
|
|
|
- </details>
|
|
|
|
|
- <p stm-if="saddles.is_admin && saddle.capabilities.length > 0">Capabilities:
|
|
|
|
|
- <span stm-for-capability-in="saddle.capabilities" stm-key="capability.link_id">
|
|
|
|
|
- <span class="badge"><span stm-text="capability.name">tag</span>
|
|
|
|
|
- <button type="button" class="btn-ghost chip-remove"
|
|
|
|
|
- stm-action="capability.detach" stm-confirm="Remove this capability?">×</button></span>
|
|
|
|
|
- </span>
|
|
|
|
|
- </p>
|
|
|
|
|
- <p stm-if="!saddles.is_admin && saddle.capabilities.length > 0">Capabilities:
|
|
|
|
|
- <span stm-for-capability-in="saddle.capabilities" stm-key="capability.link_id">
|
|
|
|
|
- <span class="badge"><span stm-text="capability.name">tag</span></span>
|
|
|
|
|
- </span>
|
|
|
|
|
- </p>
|
|
|
|
|
- <p stm-if="saddle.capabilities.length == 0" class="text-muted">No capabilities — matches only tasks with none required.</p>
|
|
|
|
|
- <form class="cluster" stm-if="saddles.is_admin && saddles.capability_options.length > 0"
|
|
|
|
|
- stm-action="saddle.attach_capability">
|
|
|
|
|
- <label stm-attribute.for="'cap-' + saddle.id">Add capability</label>
|
|
|
|
|
- <select stm-attribute.id="'cap-' + saddle.id" name="capability_id" required>
|
|
|
|
|
- <option stm-for-option-in="saddles.capability_options" stm-attribute.value="option.id"
|
|
|
|
|
- stm-text="option.name">capability</option>
|
|
|
|
|
- </select>
|
|
|
|
|
- <button type="submit">Add</button>
|
|
|
|
|
- </form>
|
|
|
|
|
- <form class="cluster">
|
|
|
|
|
- <button type="button" stm-if="saddles.is_admin" stm-action="saddle.refresh">Refresh status</button>
|
|
|
|
|
- <button type="button" stm-if="saddles.is_admin" stm-action="saddle.unregister"
|
|
|
|
|
- stm-confirm="Remove this saddle? A running task is re-queued.">Remove</button>
|
|
|
|
|
- </form>
|
|
|
|
|
|
|
+ <div class="card-header">
|
|
|
|
|
+ <h3 stm-text="saddle.name">name</h3>
|
|
|
|
|
+ <p class="cluster">
|
|
|
|
|
+ <span stm-attribute.class="'badge badge-' + saddle.status" stm-text="saddle.status">status</span>
|
|
|
|
|
+ <span class="text-muted" stm-text="saddle.host">host</span>
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="card-body stack">
|
|
|
|
|
+ <!-- The one-line health verdict consolidates the
|
|
|
|
|
+ old three fragments; the badge keeps the
|
|
|
|
|
+ at-a-glance colour the head is for. -->
|
|
|
|
|
+ <p><span stm-attribute.class="'badge badge-health-' + saddle.health"
|
|
|
|
|
+ stm-text="saddle.health">healthy</span>
|
|
|
|
|
+ <span class="text-muted" stm-text="saddle.health_verdict">verdict</span></p>
|
|
|
|
|
+ <p stm-if="saddle.current_goal_id > 0">Supervising goal:
|
|
|
|
|
+ <a stm-attribute.href="'/goals/' + saddle.current_goal_id"
|
|
|
|
|
+ stm-text="saddle.current_goal_title">goal</a>
|
|
|
|
|
+ <span class="text-muted" stm-if="saddle.allocated_display.length > 0" stm-text="'since ' + saddle.allocated_display">since</span></p>
|
|
|
|
|
+ <p stm-if="saddle.current_task_id > 0 && saddle.current_goal_id == 0">Current task:
|
|
|
|
|
+ <a stm-attribute.href="'/tasks/' + saddle.current_task_id" stm-text="saddle.current_task_title">task</a>
|
|
|
|
|
+ <span class="text-muted" stm-if="saddle.allocated_display.length > 0" stm-text="'since ' + saddle.allocated_display">since</span></p>
|
|
|
|
|
+ <p stm-if="saddle.current_task_id == 0" class="text-muted">No current task</p>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- The admin tail: everything below the fold
|
|
|
|
|
+ folds under ONE expander; the inner
|
|
|
|
|
+ blocks keep their own details so the
|
|
|
|
|
+ disclosure stays gradual. -->
|
|
|
|
|
+ <details stm-if="saddles.is_admin" class="saddle-fold">
|
|
|
|
|
+ <summary>Keys & admin</summary>
|
|
|
|
|
+ <div class="stack saddle-admin">
|
|
|
|
|
+ <p class="text-muted" stm-if="saddle.token.length > 0">API token:
|
|
|
|
|
+ <code stm-text="saddle.token">token</code></p>
|
|
|
|
|
+ <details stm-if="saddle.ssh_public_key.length > 0">
|
|
|
|
|
+ <summary>SSH public key</summary>
|
|
|
|
|
+ <p class="text-muted">Add this as a <strong>deploy key</strong> on the git server
|
|
|
|
|
+ (with write access) so this saddle can clone and push.</p>
|
|
|
|
|
+ <pre class="ssh-key"><code stm-text="saddle.ssh_public_key">key</code></pre>
|
|
|
|
|
+ <button type="button"
|
|
|
|
|
+ stm-attribute.data-clipboard="saddle.ssh_public_key"
|
|
|
|
|
+ onclick="navigator.clipboard.writeText(this.getAttribute('data-clipboard')).then(function () { this.textContent = 'Copied'; }.bind(this))">Copy key</button>
|
|
|
|
|
+ </details>
|
|
|
|
|
+ <p stm-if="saddle.ssh_public_key.length == 0" class="text-muted">No SSH key reported yet —
|
|
|
|
|
+ it appears here once the daemon generates one.</p>
|
|
|
|
|
+ <details>
|
|
|
|
|
+ <summary>Git identity</summary>
|
|
|
|
|
+ <form stm-action="saddle.update_identity" class="cluster">
|
|
|
|
|
+ <label>Committed as</label>
|
|
|
|
|
+ <input type="text" name="git_name" stm-attribute.value="saddle.git_name"
|
|
|
|
|
+ placeholder="saddle-1">
|
|
|
|
|
+ <input type="text" name="git_email" stm-attribute.value="saddle.git_email"
|
|
|
|
|
+ placeholder="saddle-1@example.net">
|
|
|
|
|
+ <button type="submit" class="btn-sm">Save</button>
|
|
|
|
|
+ <span class="text-muted">Empty fields fall back to the daemon's default identity.</span>
|
|
|
|
|
+ </form>
|
|
|
|
|
+ </details>
|
|
|
|
|
+ <details>
|
|
|
|
|
+ <summary>Prompt note</summary>
|
|
|
|
|
+ <form stm-action="saddle.update_prompt_note" class="cluster">
|
|
|
|
|
+ <label for="prompt-note">Served inside every assignment's system prompt</label>
|
|
|
|
|
+ <textarea id="prompt-note" name="prompt_note" rows="3"
|
|
|
|
|
+ stm-attribute.placeholder="'e.g. You have root and may install development tooling via DNF'"
|
|
|
|
|
+ stm-text="saddle.prompt_note"></textarea>
|
|
|
|
|
+ <button type="submit" class="btn-sm">Save</button>
|
|
|
|
|
+ <span class="text-muted">A standing message about THIS machine for its agents; empty removes it (capped at 2000 chars).</span>
|
|
|
|
|
+ </form>
|
|
|
|
|
+ </details>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <p stm-if="saddle.capabilities.length > 0">Capabilities:
|
|
|
|
|
+ <span stm-for-capability-in="saddle.capabilities" stm-key="capability.link_id">
|
|
|
|
|
+ <span class="badge"><span stm-text="capability.name">tag</span>
|
|
|
|
|
+ <button type="button" class="btn-ghost chip-remove"
|
|
|
|
|
+ stm-action="capability.detach" stm-confirm="Remove this capability?">×</button></span>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </p>
|
|
|
|
|
+ <p stm-if="saddle.capabilities.length == 0" class="text-muted">No capabilities — matches only tasks with none required.</p>
|
|
|
|
|
+ <form class="cluster" stm-if="saddles.capability_options.length > 0"
|
|
|
|
|
+ stm-action="saddle.attach_capability">
|
|
|
|
|
+ <label stm-attribute.for="'cap-' + saddle.id">Add capability</label>
|
|
|
|
|
+ <select stm-attribute.id="'cap-' + saddle.id" name="capability_id" required>
|
|
|
|
|
+ <option stm-for-option-in="saddles.capability_options" stm-attribute.value="option.id"
|
|
|
|
|
+ stm-text="option.name">capability</option>
|
|
|
|
|
+ </select>
|
|
|
|
|
+ <button type="submit" class="btn-sm">Add</button>
|
|
|
|
|
+ </form>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <form class="cluster">
|
|
|
|
|
+ <button type="button" class="btn-sm" stm-action="saddle.refresh">Refresh status</button>
|
|
|
|
|
+ <button type="button" class="btn-sm btn-danger" stm-action="saddle.unregister"
|
|
|
|
|
+ stm-confirm="Remove this saddle? A running task is re-queued.">Remove</button>
|
|
|
|
|
+ </form>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </details>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- Non-admins see the routing tags without any
|
|
|
|
|
+ admin machinery (the roles decision). -->
|
|
|
|
|
+ <p stm-if="!saddles.is_admin && saddle.capabilities.length > 0">Capabilities:
|
|
|
|
|
+ <span stm-for-capability-in="saddle.capabilities" stm-key="capability.link_id">
|
|
|
|
|
+ <span class="badge"><span stm-text="capability.name">tag</span></span>
|
|
|
|
|
+ </span>
|
|
|
|
|
+ </p>
|
|
|
|
|
+ </div>
|
|
|
</section>
|
|
</section>
|
|
|
<section class="card" stm-if="saddles.saddles.length == 0">
|
|
<section class="card" stm-if="saddles.saddles.length == 0">
|
|
|
<p class="text-muted">No saddles registered. The dispatcher pairs queued tasks with idle saddles automatically.</p>
|
|
<p class="text-muted">No saddles registered. The dispatcher pairs queued tasks with idle saddles automatically.</p>
|
|
|
</section>
|
|
</section>
|
|
|
</div>
|
|
</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
|
|
|
- <h3>Capabilities</h3>
|
|
|
|
|
|
|
+ <!-- The capability catalogue (recommendation C): its own
|
|
|
|
|
+ h2-level section, a different modality than the card
|
|
|
|
|
+ grid above, so the page reads as two deliberate
|
|
|
|
|
+ sections. -->
|
|
|
|
|
+ <div class="stack" stm-if="saddles != null">
|
|
|
|
|
+ <h2 id="routing-tags">Routing tags</h2>
|
|
|
<p class="text-muted">Tags the dispatcher routes by: a task is only assigned to a saddle carrying every
|
|
<p class="text-muted">Tags the dispatcher routes by: a task is only assigned to a saddle carrying every
|
|
|
tag the task requires. Manage a saddle's tags on its card above; require tags on a task's page.</p>
|
|
tag the task requires. Manage a saddle's tags on its card above; require tags on a task's page.</p>
|
|
|
- <details stm-if="saddles.is_admin">
|
|
|
|
|
- <summary><strong>New capability</strong></summary>
|
|
|
|
|
- <form stm-action="saddles.create_capability" class="cluster">
|
|
|
|
|
- <label for="new-capability">Name</label>
|
|
|
|
|
- <input type="text" id="new-capability" name="name" required placeholder="gpu">
|
|
|
|
|
- <button type="submit">Create capability</button>
|
|
|
|
|
- </form>
|
|
|
|
|
- </details>
|
|
|
|
|
|
|
+ <div class="cluster">
|
|
|
|
|
+ <button type="button" class="btn-primary" stm-if="saddles.is_admin"
|
|
|
|
|
+ onclick="window.openAppDialog('capability-create-dialog')">+ New tag</button>
|
|
|
|
|
+ </div>
|
|
|
<div class="table-wrap">
|
|
<div class="table-wrap">
|
|
|
- <table>
|
|
|
|
|
|
|
+ <table class="table-hover">
|
|
|
|
|
|
|
|
<thead><tr><th>Tag</th><th>Saddles carrying it</th><th>Tasks requiring it</th><th></th></tr></thead>
|
|
<thead><tr><th>Tag</th><th>Saddles carrying it</th><th>Tasks requiring it</th><th></th></tr></thead>
|
|
|
<tbody>
|
|
<tbody>
|
|
@@ -130,17 +165,62 @@
|
|
|
<td><span class="badge" stm-text="capability.name">tag</span></td>
|
|
<td><span class="badge" stm-text="capability.name">tag</span></td>
|
|
|
<td stm-text="capability.saddle_count">0</td>
|
|
<td stm-text="capability.saddle_count">0</td>
|
|
|
<td stm-text="capability.task_count">0</td>
|
|
<td stm-text="capability.task_count">0</td>
|
|
|
- <td stm-if="saddles.is_admin"><button type="button" stm-action="capability.delete"
|
|
|
|
|
|
|
+ <td stm-if="saddles.is_admin"><button type="button" class="btn-sm btn-danger" stm-action="capability.delete"
|
|
|
stm-confirm="Delete this capability everywhere?">Delete</button></td>
|
|
stm-confirm="Delete this capability everywhere?">Delete</button></td>
|
|
|
</tr>
|
|
</tr>
|
|
|
<tr stm-if="saddles.capabilities.length == 0">
|
|
<tr stm-if="saddles.capabilities.length == 0">
|
|
|
<td colspan="4" class="text-muted">No capabilities defined yet.</td>
|
|
<td colspan="4" class="text-muted">No capabilities defined yet.</td>
|
|
|
</tr>
|
|
</tr>
|
|
|
</tbody>
|
|
</tbody>
|
|
|
-
|
|
|
|
|
|
|
+
|
|
|
</table>
|
|
</table>
|
|
|
</div>
|
|
</div>
|
|
|
</div>
|
|
</div>
|
|
|
- </section>
|
|
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- Goal 41 T8: the register form moved from a collapsed expander
|
|
|
|
|
+ into a Spry <dialog> behind the "+ Register saddle" button -
|
|
|
|
|
+ same fields, same stm-action (the shared openAppDialog helper
|
|
|
|
|
+ closes it on the action's notify). -->
|
|
|
|
|
+ <dialog id="saddle-register-dialog" class="dialog-sm">
|
|
|
|
|
+ <div class="dialog-titlebar">
|
|
|
|
|
+ <p class="dialog-title">Register saddle</p>
|
|
|
|
|
+ <button type="button" class="dialog-close" aria-label="Close"
|
|
|
|
|
+ onclick="window.closeAppDialog('saddle-register-dialog')">×</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <form stm-action="saddles.register">
|
|
|
|
|
+ <div class="dialog-body form-rows">
|
|
|
|
|
+ <div class="field"><label for="saddle-name">Name</label>
|
|
|
|
|
+ <input type="text" id="saddle-name" name="name" required placeholder="saddle-2"></div>
|
|
|
|
|
+ <div class="field"><label for="saddle-host">Host</label>
|
|
|
|
|
+ <input type="text" id="saddle-host" name="host" required placeholder="localhost:7100"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="dialog-footer">
|
|
|
|
|
+ <button type="button" class="btn-ghost"
|
|
|
|
|
+ onclick="window.closeAppDialog('saddle-register-dialog')">Cancel</button>
|
|
|
|
|
+ <button type="submit" class="btn-primary">Register</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </form>
|
|
|
|
|
+ </dialog>
|
|
|
|
|
+
|
|
|
|
|
+ <!-- The capability create form, same treatment. -->
|
|
|
|
|
+ <dialog id="capability-create-dialog" class="dialog-sm">
|
|
|
|
|
+ <div class="dialog-titlebar">
|
|
|
|
|
+ <p class="dialog-title">New routing tag</p>
|
|
|
|
|
+ <button type="button" class="dialog-close" aria-label="Close"
|
|
|
|
|
+ onclick="window.closeAppDialog('capability-create-dialog')">×</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <form stm-action="saddles.create_capability">
|
|
|
|
|
+ <div class="dialog-body form-rows">
|
|
|
|
|
+ <div class="field"><label for="new-capability">Name</label>
|
|
|
|
|
+ <input type="text" id="new-capability" name="name" required placeholder="gpu"></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="dialog-footer">
|
|
|
|
|
+ <button type="button" class="btn-ghost"
|
|
|
|
|
+ onclick="window.closeAppDialog('capability-create-dialog')">Cancel</button>
|
|
|
|
|
+ <button type="submit" class="btn-primary">Create tag</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </form>
|
|
|
|
|
+ </dialog>
|
|
|
</body>
|
|
</body>
|
|
|
</html>
|
|
</html>
|