@ssm123ssm/vault 0.1.16 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/preview-agent.js +212 -26
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ssm123ssm/vault",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "private": false,
5
5
  "description": "Preview Vault agent CLI",
6
6
  "bin": {
package/preview-agent.js CHANGED
@@ -235,36 +235,206 @@ async function selectDirectoryViaServer() {
235
235
  <meta name="viewport" content="width=device-width, initial-scale=1" />
236
236
  <title>Preview Agent - Select Folder</title>
237
237
  <style>
238
- body { font-family: ui-sans-serif, system-ui; background:#0f1117; color:#f3f1eb; margin:0; }
239
- .wrap { max-width: 720px; margin: 0 auto; padding: 24px; }
240
- .card { background:#151a24; border:1px solid rgba(255,255,255,0.08); border-radius:16px; padding:16px; }
241
- .path { font-family: ui-monospace, SFMono-Regular; font-size: 12px; }
242
- .list { margin-top:12px; display:grid; gap:8px; }
243
- button { background:#222837; color:#f3f1eb; border:1px solid rgba(255,255,255,0.12); border-radius:12px; padding:8px 12px; cursor:pointer; }
244
- button:hover { border-color:#f97316; }
245
- .primary { background:#f97316; color:#1a1410; font-weight:600; }
246
- .row { display:flex; gap:8px; align-items:center; flex-wrap:wrap; }
238
+ :root {
239
+ --bg: #0b0f14;
240
+ --bg-2: #0f1624;
241
+ --panel: #141c2b;
242
+ --panel-2: #0f1522;
243
+ --text: #e9edf2;
244
+ --muted: #94a3b8;
245
+ --accent: #ff7a18;
246
+ --accent-2: #ffb347;
247
+ --border: rgba(255,255,255,0.08);
248
+ }
249
+ * { box-sizing: border-box; }
250
+ body {
251
+ margin: 0;
252
+ color: var(--text);
253
+ font-family: "Space Grotesk", "Avenir Next", "Segoe UI", sans-serif;
254
+ background:
255
+ radial-gradient(1100px 600px at 15% -10%, rgba(255, 179, 71, 0.16), transparent 60%),
256
+ radial-gradient(900px 500px at 90% 0%, rgba(255, 122, 24, 0.18), transparent 55%),
257
+ linear-gradient(180deg, var(--bg), var(--bg-2));
258
+ }
259
+ .wrap { max-width: 900px; margin: 0 auto; padding: 28px 24px 40px; }
260
+ header { display:flex; align-items:center; justify-content:space-between; gap:16px; margin-bottom: 18px; }
261
+ h2 { margin: 0; font-size: 22px; letter-spacing: 0.3px; }
262
+ .subtitle { color: var(--muted); font-size: 13px; }
263
+ .panel {
264
+ background: linear-gradient(160deg, rgba(255,255,255,0.02), rgba(255,255,255,0.01));
265
+ border: 1px solid var(--border);
266
+ border-radius: 18px;
267
+ padding: 16px;
268
+ box-shadow: 0 20px 50px rgba(0,0,0,0.35);
269
+ }
270
+ .toolbar { display:flex; flex-wrap:wrap; gap:10px; align-items:center; }
271
+ .pathline {
272
+ margin-top: 10px;
273
+ display:flex;
274
+ align-items:center;
275
+ gap:10px;
276
+ padding: 10px 12px;
277
+ border-radius: 12px;
278
+ background: var(--panel-2);
279
+ border: 1px solid var(--border);
280
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, "Liberation Mono", monospace;
281
+ font-size: 12px;
282
+ color: #d6dee8;
283
+ overflow-x: auto;
284
+ }
285
+ .breadcrumbs { display:flex; flex-wrap:wrap; gap:6px; margin-top: 10px; }
286
+ .crumb {
287
+ border: 1px solid transparent;
288
+ background: rgba(255,255,255,0.05);
289
+ color: #e2e8f0;
290
+ padding: 6px 10px;
291
+ border-radius: 999px;
292
+ cursor: pointer;
293
+ font-size: 12px;
294
+ }
295
+ .crumb:hover { border-color: rgba(255,255,255,0.2); }
296
+ .list { margin-top:14px; display:grid; gap:10px; }
297
+ .entry {
298
+ display:flex;
299
+ align-items:center;
300
+ justify-content:space-between;
301
+ width: 100%;
302
+ background: var(--panel);
303
+ color: var(--text);
304
+ border: 1px solid var(--border);
305
+ border-radius: 14px;
306
+ padding: 12px 14px;
307
+ cursor: pointer;
308
+ text-align: left;
309
+ }
310
+ .entry:hover { border-color: rgba(255, 122, 24, 0.6); }
311
+ .entry span { font-size: 14px; }
312
+ .entry small { color: var(--muted); }
313
+ .muted { color: var(--muted); font-size: 12px; }
314
+ .actions { display:flex; gap:10px; flex-wrap:wrap; margin-top: 16px; }
315
+ button {
316
+ background: rgba(255,255,255,0.06);
317
+ color: var(--text);
318
+ border: 1px solid var(--border);
319
+ border-radius: 12px;
320
+ padding: 10px 14px;
321
+ cursor: pointer;
322
+ font-size: 13px;
323
+ transition: border-color 0.2s ease, transform 0.2s ease;
324
+ }
325
+ button:hover { border-color: rgba(255, 122, 24, 0.7); transform: translateY(-1px); }
326
+ button:disabled { opacity: 0.5; cursor: not-allowed; transform: none; }
327
+ .primary {
328
+ background: linear-gradient(135deg, var(--accent), var(--accent-2));
329
+ color: #1b1209;
330
+ font-weight: 600;
331
+ border: none;
332
+ }
333
+ .ghost { background: transparent; }
334
+ input[type="text"] {
335
+ background: rgba(10, 14, 20, 0.6);
336
+ border: 1px solid var(--border);
337
+ border-radius: 12px;
338
+ padding: 10px 12px;
339
+ color: var(--text);
340
+ font-size: 13px;
341
+ min-width: 220px;
342
+ }
343
+ .footer { margin-top: 14px; font-size: 12px; color: var(--muted); }
247
344
  </style>
248
345
  </head>
249
346
  <body>
250
347
  <div class="wrap">
251
- <h2>Select a folder for this project</h2>
252
- <div class="card">
253
- <div class="row">
254
- <button id="upBtn">Up</button>
255
- <button id="useBtn" class="primary">Use this folder</button>
348
+ <header>
349
+ <div>
350
+ <h2>Choose a folder</h2>
351
+ <div class="subtitle">Pick the folder that should be linked to this project.</div>
352
+ </div>
353
+ <div class="muted" id="status">Waiting for selection…</div>
354
+ </header>
355
+ <div class="panel">
356
+ <div class="toolbar">
357
+ <button id="upBtn" class="ghost">Up one level</button>
358
+ <input id="filter" type="text" placeholder="Filter folders…" />
256
359
  </div>
257
- <div class="path" id="path"></div>
360
+ <div class="pathline" id="path"></div>
361
+ <div class="breadcrumbs" id="breadcrumbs"></div>
258
362
  <div class="list" id="list"></div>
363
+ <div class="actions">
364
+ <button id="confirmBtn" class="primary">Confirm selection</button>
365
+ <button id="closeBtn" class="ghost">Close window</button>
366
+ </div>
259
367
  </div>
260
- <p style="font-size:12px;opacity:0.7;margin-top:12px;">Leave this window open until selection is confirmed.</p>
368
+ <div class="footer">Leave this window open until selection is confirmed. Closing it early will cancel the assignment.</div>
261
369
  </div>
262
370
  <script>
263
371
  let currentPath = "";
372
+ let allEntries = [];
264
373
  const pathEl = document.getElementById("path");
265
374
  const listEl = document.getElementById("list");
266
375
  const upBtn = document.getElementById("upBtn");
267
- const useBtn = document.getElementById("useBtn");
376
+ const confirmBtn = document.getElementById("confirmBtn");
377
+ const closeBtn = document.getElementById("closeBtn");
378
+ const statusEl = document.getElementById("status");
379
+ const filterEl = document.getElementById("filter");
380
+ const crumbsEl = document.getElementById("breadcrumbs");
381
+
382
+ function splitPathSegments(input) {
383
+ const hasBackslash = input.includes("\\\\") && !input.includes("/");
384
+ const sep = hasBackslash ? "\\\\" : "/";
385
+ const parts = input.split(sep).filter(Boolean);
386
+ const prefix = !hasBackslash && input.startsWith("/") ? "/" : "";
387
+ return { parts, sep, prefix };
388
+ }
389
+
390
+ function renderBreadcrumbs(pathValue) {
391
+ crumbsEl.innerHTML = "";
392
+ const { parts, sep, prefix } = splitPathSegments(pathValue);
393
+ if (!parts.length) return;
394
+ let acc = prefix || "";
395
+ parts.forEach((part, index) => {
396
+ if (!acc) {
397
+ acc = part;
398
+ } else if (acc === "/" || acc.endsWith(sep)) {
399
+ acc = acc + part;
400
+ } else {
401
+ acc = acc + sep + part;
402
+ }
403
+ const btn = document.createElement("button");
404
+ btn.className = "crumb";
405
+ btn.textContent = part;
406
+ btn.onclick = () => load(acc);
407
+ crumbsEl.appendChild(btn);
408
+ if (index < parts.length - 1) {
409
+ const dot = document.createElement("span");
410
+ dot.className = "muted";
411
+ dot.textContent = "›";
412
+ crumbsEl.appendChild(dot);
413
+ }
414
+ });
415
+ }
416
+
417
+ function renderList() {
418
+ const term = (filterEl.value || "").trim().toLowerCase();
419
+ listEl.innerHTML = "";
420
+ const entries = allEntries.filter((entry) =>
421
+ entry.name.toLowerCase().includes(term)
422
+ );
423
+ if (!entries.length) {
424
+ const empty = document.createElement("div");
425
+ empty.className = "muted";
426
+ empty.textContent = "No folders match this filter.";
427
+ listEl.appendChild(empty);
428
+ return;
429
+ }
430
+ entries.forEach((entry) => {
431
+ const btn = document.createElement("button");
432
+ btn.className = "entry";
433
+ btn.innerHTML = "<span>" + entry.name + "/</span><small>Open</small>";
434
+ btn.onclick = () => load(entry.path);
435
+ listEl.appendChild(btn);
436
+ });
437
+ }
268
438
 
269
439
  async function load(path) {
270
440
  const res = await fetch("/api/list?path=" + encodeURIComponent(path || ""));
@@ -272,25 +442,33 @@ async function selectDirectoryViaServer() {
272
442
  if (!res.ok) return;
273
443
  currentPath = data.path;
274
444
  pathEl.textContent = currentPath;
275
- listEl.innerHTML = "";
276
- data.entries.forEach((entry) => {
277
- const btn = document.createElement("button");
278
- btn.textContent = entry.name + "/";
279
- btn.onclick = () => load(entry.path);
280
- listEl.appendChild(btn);
281
- });
445
+ allEntries = data.entries || [];
446
+ renderList();
447
+ renderBreadcrumbs(currentPath);
282
448
  upBtn.onclick = () => load(data.parent);
449
+ confirmBtn.disabled = !currentPath;
283
450
  }
284
451
 
285
- useBtn.onclick = async () => {
452
+ confirmBtn.onclick = async () => {
453
+ if (!currentPath) return;
454
+ statusEl.textContent = "Confirming selection…";
286
455
  await fetch("/api/select", {
287
456
  method: "POST",
288
457
  headers: { "Content-Type": "application/json" },
289
458
  body: JSON.stringify({ path: currentPath }),
290
459
  });
291
- useBtn.textContent = "Selected";
460
+ confirmBtn.textContent = "Confirmed";
461
+ statusEl.textContent = "Selection confirmed. You can close this window.";
462
+ setTimeout(() => window.close(), 150);
292
463
  };
293
464
 
465
+ closeBtn.onclick = () => {
466
+ window.close();
467
+ statusEl.textContent = "You can close this window now.";
468
+ };
469
+
470
+ filterEl.addEventListener("input", () => renderList());
471
+
294
472
  load("");
295
473
  </script>
296
474
  </body>
@@ -786,6 +964,14 @@ function runDaemon() {
786
964
  const sourceDir = daemonMode || uiMode
787
965
  ? await selectDirectoryViaServer()
788
966
  : await selectDirectory(process.cwd());
967
+ const selectionMode = daemonMode
968
+ ? "daemon"
969
+ : uiMode
970
+ ? "ui"
971
+ : "interactive";
972
+ console.log(
973
+ `[${agentLabel}] Selected folder (${selectionMode}): ${sourceDir}`
974
+ );
789
975
  config.projects = config.projects || {};
790
976
  config.projects[command.projectId] = {
791
977
  source: sourceDir,