@cr8rcho/alkahest 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.
@@ -56,24 +56,53 @@
56
56
  border-bottom: 1px solid var(--line);
57
57
  background: var(--panel);
58
58
  flex: 0 0 auto;
59
+ position: relative; /* anchor the Display dropdown above the graph */
60
+ z-index: 10;
59
61
  }
60
- .theme-toggle { cursor: pointer; border: 1px solid var(--line); background: var(--bg); color: var(--ink); border-radius: 7px; padding: 4px 9px; font-size: 12px; }
61
- /* Fit + theme toggle are matched 32×32 icon buttons (Fit = expand-to-corners; toggle = moon/sun). */
62
- #theme-toggle, #fit-btn { display: inline-flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 8px; background: var(--panel); padding: 0; }
63
- #theme-toggle:hover, #fit-btn:hover { border-color: var(--accent); }
64
- #theme-toggle svg, #fit-btn svg { width: 16px; height: 16px; }
62
+ /* All three toolbar buttons (Display, Fit, theme) share one chip style on --panel. */
63
+ .tbtn { display: inline-flex; align-items: center; justify-content: center; height: 32px; border-radius: 8px; background: var(--panel); border: 1px solid var(--line); color: var(--ink); cursor: pointer; padding: 0; }
64
+ .tbtn:hover { border-color: var(--accent); }
65
+ .tbtn svg { width: 16px; height: 16px; }
66
+ #fit-btn, #theme-toggle { width: 32px; }
65
67
  [data-theme="dark"] #theme-toggle .i-sun { display: block; } [data-theme="dark"] #theme-toggle .i-moon { display: none; }
66
68
  [data-theme="light"] #theme-toggle .i-sun { display: none; } [data-theme="light"] #theme-toggle .i-moon { display: block; }
67
- /* Fit + toggle are a tight pair (8px), set apart from the rest of the toolbar by its 16px gap. */
68
- .toolbar-actions { display: inline-flex; align-items: center; gap: 8px; }
69
+ /* Action cluster pinned to the right: Display · Fit · theme */
70
+ .toolbar-actions { margin-left: auto; display: inline-flex; align-items: center; gap: 8px; }
69
71
  .brand { font-weight: 700; letter-spacing: 0.3px; white-space: nowrap; }
70
72
  .brand .sub { color: var(--muted); font-weight: 400; margin-left: 6px; }
71
73
  .counts { color: var(--muted); white-space: nowrap; }
72
- .toolbar label { display: inline-flex; align-items: center; gap: 5px; cursor: pointer; user-select: none; white-space: nowrap; }
73
- .legend { margin-left: auto; display: flex; flex-wrap: wrap; gap: 6px 14px; color: var(--muted); align-items: center; }
74
- .legend span { white-space: nowrap; }
75
- .legend .swatch { display: inline-block; width: 10px; height: 10px; border-radius: 50%; margin-right: 5px; vertical-align: -1px; }
76
- .legend .edge { display: inline-block; width: 22px; border-top: 2px solid; margin-right: 5px; vertical-align: 3px; }
74
+ /* compact counts (mobile only): node-shape icons + number screen / resource, matching the legend */
75
+ .counts-min { display: none; }
76
+ .counts-min .cg { display: inline-flex; align-items: center; gap: 5px; }
77
+ .counts-min .cg + .cg { margin-left: 11px; }
78
+ .counts-min .nd { width: 9px; height: 9px; border: 1.5px solid var(--node-stroke); background: var(--node-fill); flex: 0 0 auto; }
79
+ .counts-min .nd.dot { border-radius: 50%; }
80
+ .counts-min .nd.sq { border-radius: 2px; background: var(--res-fill); }
81
+
82
+ /* ---- Display menu: edge filters + legend behind one button. Dropdown on desktop, bottom sheet on mobile. ---- */
83
+ .display-menu { position: relative; }
84
+ .display-btn { gap: 7px; padding: 0 11px; }
85
+ .display-label { font-size: 12px; }
86
+ .display-menu.open .display-btn { border-color: var(--accent); color: var(--accent); }
87
+ .display-backdrop { display: none; }
88
+ .display-pop {
89
+ position: absolute; top: calc(100% + 8px); right: 0; z-index: 30;
90
+ min-width: 210px; background: var(--panel-soft); border: 1px solid var(--line);
91
+ border-radius: 11px; padding: 12px 14px; box-shadow: 0 14px 36px rgba(0, 0, 0, 0.28);
92
+ display: none;
93
+ }
94
+ .display-menu.open .display-pop { display: block; }
95
+ .display-pop h3 { font-size: 10px; text-transform: uppercase; letter-spacing: 0.6px; color: var(--muted); margin: 0 0 8px; }
96
+ .display-pop h3.grp { margin-top: 13px; }
97
+ .disp-row { display: flex; align-items: center; gap: 8px; padding: 5px 0; cursor: pointer; white-space: nowrap; user-select: none; }
98
+ .disp-legend { display: flex; flex-direction: column; gap: 6px; color: var(--muted); }
99
+ .disp-legend > span { white-space: nowrap; }
100
+ .disp-row .sw, .disp-legend .sw { display: inline-block; width: 10px; height: 10px; border-radius: 50%; border: 1.5px solid var(--node-stroke); vertical-align: -1px; }
101
+ .disp-legend .sw { margin-right: 6px; }
102
+ .sw.res { border-radius: 2px; }
103
+ .disp-row .lkey, .disp-legend .lkey { display: inline-block; width: 22px; border-top: 2px solid var(--edge); vertical-align: 3px; }
104
+ .disp-legend .lkey { margin-right: 6px; }
105
+ .lkey.dot { border-top-style: dotted; } .lkey.dash { border-top-style: dashed; }
77
106
  main { flex: 1 1 auto; display: flex; min-height: 0; position: relative; }
78
107
  #graph { flex: 1 1 auto; min-width: 0; cursor: grab; touch-action: none; }
79
108
  #graph:active { cursor: grabbing; }
@@ -117,9 +146,27 @@
117
146
  /* ---- Mobile: on narrow screens, render the panel as a bottom sheet ---- */
118
147
  @media (max-width: 640px) {
119
148
  body { height: 100dvh; }
120
- .toolbar { column-gap: 12px; padding: 8px 12px; font-size: 12px; }
149
+ .toolbar { column-gap: 10px; padding: 8px 12px; font-size: 12px; flex-wrap: nowrap; }
121
150
  .brand .sub { display: none; }
122
- .legend { width: 100%; margin-left: 0; justify-content: flex-start; font-size: 11px; }
151
+ .counts-full { display: none; }
152
+ .counts-min { display: inline-flex; align-items: center; }
153
+ .display-label { display: none; } /* Display collapses to icon-only */
154
+ .display-btn { width: 32px; padding: 0; }
155
+ /* Display popover → bottom sheet, mirroring the #panel idiom */
156
+ .display-menu.open .display-backdrop { display: block; position: fixed; inset: 0; background: rgba(0, 0, 0, 0.35); z-index: 40; }
157
+ .display-pop {
158
+ position: fixed; left: 0; right: 0; bottom: 0; top: auto;
159
+ min-width: 0; width: 100%;
160
+ border-radius: 16px 16px 0 0; border-left: none; border-right: none; border-bottom: none;
161
+ box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.45);
162
+ padding: 26px 18px 22px; z-index: 41;
163
+ transform: translateY(101%); transition: transform 0.24s ease;
164
+ display: block; /* always present; slid off-screen until open */
165
+ }
166
+ .display-menu.open .display-pop { transform: translateY(0); }
167
+ .display-pop::before { content: ""; position: absolute; top: 9px; left: 50%; width: 40px; height: 4px; margin-left: -20px; background: var(--line); border-radius: 2px; }
168
+ .disp-row { padding: 9px 0; font-size: 14px; }
169
+ .disp-legend { font-size: 13px; gap: 8px; }
123
170
  #panel {
124
171
  position: absolute;
125
172
  left: 0; right: 0; bottom: 0;
@@ -156,20 +203,28 @@
156
203
  <body>
157
204
  <header class="toolbar">
158
205
  <div class="brand">Alkahest<span class="sub">Product Map</span></div>
159
- <div class="counts" id="counts"></div>
160
- <label><input type="checkbox" id="t-trans" checked /> Navigate</label>
161
- <label><input type="checkbox" id="t-calls" checked /> Call</label>
162
- <div class="legend">
163
- <span style="font-weight:700">▶ Start</span>
164
- <span><span class="swatch" style="background: var(--node-fill); border: 1.5px solid var(--node-stroke)"></span>Screen</span>
165
- <span><span class="swatch" style="background: var(--res-fill); border: 1.5px solid var(--node-stroke); border-radius: 2px"></span>Resource</span>
166
- <span><span class="edge" style="border-color: var(--edge)"></span>Navigate</span>
167
- <span><span class="edge" style="border-top-style: dotted; border-color: var(--edge)"></span>Contains</span>
168
- <span><span class="edge" style="border-top-style: dashed; border-color: var(--edge)"></span>Call</span>
169
- </div>
206
+ <div class="counts" id="counts"><span class="counts-full"></span><span class="counts-min"></span></div>
170
207
  <div class="toolbar-actions">
171
- <button class="theme-toggle" id="fit-btn" aria-label="Fit to view" title="Fit to view"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3M16 3h3a2 2 0 0 1 2 2v3M8 21H5a2 2 0 0 1-2-2v-3M16 21h3a2 2 0 0 0 2-2v-3"/></svg></button>
172
- <button class="theme-toggle" id="theme-toggle" aria-label="Toggle theme" title="Toggle theme"><svg class="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg><svg class="i-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg></button>
208
+ <div class="display-menu" id="display-menu">
209
+ <button class="tbtn display-btn" id="display-btn" aria-haspopup="true" aria-expanded="false" aria-label="Display options" title="Display options"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round"><line x1="4" y1="8" x2="20" y2="8"/><circle cx="9" cy="8" r="2.4" fill="var(--panel)"/><line x1="4" y1="16" x2="20" y2="16"/><circle cx="15" cy="16" r="2.4" fill="var(--panel)"/></svg><span class="display-label">Display</span></button>
210
+ <div class="display-backdrop" id="display-backdrop"></div>
211
+ <div class="display-pop" id="display-pop" role="menu">
212
+ <h3>Show</h3>
213
+ <label class="disp-row"><input type="checkbox" id="t-trans" checked /><span class="lkey"></span>Navigate</label>
214
+ <label class="disp-row"><input type="checkbox" id="t-calls" checked /><span class="lkey dash"></span>Call</label>
215
+ <h3 class="grp">Legend</h3>
216
+ <div class="disp-legend">
217
+ <span style="font-weight:700">▶ Start</span>
218
+ <span><span class="sw"></span>Screen</span>
219
+ <span><span class="sw res"></span>Resource</span>
220
+ <span><span class="lkey"></span>Navigate</span>
221
+ <span><span class="lkey dot"></span>Contains</span>
222
+ <span><span class="lkey dash"></span>Call</span>
223
+ </div>
224
+ </div>
225
+ </div>
226
+ <button class="tbtn" id="fit-btn" aria-label="Fit to view" title="Fit to view"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M8 3H5a2 2 0 0 0-2 2v3M16 3h3a2 2 0 0 1 2 2v3M8 21H5a2 2 0 0 1-2-2v-3M16 21h3a2 2 0 0 0 2-2v-3"/></svg></button>
227
+ <button class="tbtn" id="theme-toggle" aria-label="Toggle theme" title="Toggle theme"><svg class="i-moon" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"/></svg><svg class="i-sun" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><circle cx="12" cy="12" r="4"/><path d="M12 2v2M12 20v2M4.9 4.9l1.4 1.4M17.7 17.7l1.4 1.4M2 12h2M20 12h2M4.9 19.1l1.4-1.4M17.7 6.3l1.4-1.4"/></svg></button>
173
228
  </div>
174
229
  </header>
175
230
  <main>
@@ -264,10 +319,15 @@
264
319
  {
265
320
  const navCount = MAP.transitions.filter((t) => (t.kind || "navigate") === "navigate").length;
266
321
  const containCount = MAP.transitions.length - navCount;
267
- document.getElementById("counts").textContent =
322
+ const countsEl = document.getElementById("counts");
323
+ countsEl.querySelector(".counts-full").textContent =
268
324
  MAP.screens.length + " screens · " + MAP.resources.length + " resources · " +
269
325
  navCount + " navigate" + (containCount ? " · " + containCount + " contains" : "") +
270
326
  " · " + MAP.calls.length + " calls";
327
+ // compact form for the mobile toolbar: ○{screens} ▢{resources} (node-shape icons)
328
+ countsEl.querySelector(".counts-min").innerHTML =
329
+ '<span class="cg"><span class="nd dot"></span>' + MAP.screens.length + "</span>" +
330
+ '<span class="cg"><span class="nd sq"></span>' + MAP.resources.length + "</span>";
271
331
  }
272
332
 
273
333
  // ====== force-directed layout (reference approach) ======
@@ -497,6 +557,21 @@
497
557
  // ---- fit-to-view button ----
498
558
  document.getElementById("fit-btn").addEventListener("click", fitToView);
499
559
 
560
+ // ---- Display menu (edge filters + legend): dropdown on desktop, bottom sheet on mobile ----
561
+ {
562
+ const menu = document.getElementById("display-menu");
563
+ const btn = document.getElementById("display-btn");
564
+ const close = () => { menu.classList.remove("open"); btn.setAttribute("aria-expanded", "false"); };
565
+ btn.addEventListener("click", (e) => {
566
+ e.stopPropagation();
567
+ const open = menu.classList.toggle("open");
568
+ btn.setAttribute("aria-expanded", open ? "true" : "false");
569
+ });
570
+ document.getElementById("display-backdrop").addEventListener("click", close);
571
+ document.addEventListener("click", (e) => { if (!menu.contains(e.target)) close(); });
572
+ document.addEventListener("keydown", (e) => { if (e.key === "Escape") close(); });
573
+ }
574
+
500
575
  // ---- panel ----
501
576
  const panel = document.getElementById("panel-body"); // content container (close button/handle are preserved)
502
577
  const esc = (s) => String(s).replace(/[&<>]/g, (c) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;" }[c]));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cr8rcho/alkahest",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },