@cr8rcho/alkahest 0.1.16 → 0.1.17

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,48 @@
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
+ .counts-min { display: none; } /* compact "12S 8R" shown on mobile only */
75
+ .counts-min i { font-style: normal; font-size: 0.78em; opacity: 0.72; margin-left: 1px; }
76
+
77
+ /* ---- Display menu: edge filters + legend behind one button. Dropdown on desktop, bottom sheet on mobile. ---- */
78
+ .display-menu { position: relative; }
79
+ .display-btn { gap: 7px; padding: 0 11px; }
80
+ .display-label { font-size: 12px; }
81
+ .display-menu.open .display-btn { border-color: var(--accent); color: var(--accent); }
82
+ .display-backdrop { display: none; }
83
+ .display-pop {
84
+ position: absolute; top: calc(100% + 8px); right: 0; z-index: 30;
85
+ min-width: 210px; background: var(--panel-soft); border: 1px solid var(--line);
86
+ border-radius: 11px; padding: 12px 14px; box-shadow: 0 14px 36px rgba(0, 0, 0, 0.28);
87
+ display: none;
88
+ }
89
+ .display-menu.open .display-pop { display: block; }
90
+ .display-pop h3 { font-size: 10px; text-transform: uppercase; letter-spacing: 0.6px; color: var(--muted); margin: 0 0 8px; }
91
+ .display-pop h3.grp { margin-top: 13px; }
92
+ .disp-row { display: flex; align-items: center; gap: 8px; padding: 5px 0; cursor: pointer; white-space: nowrap; user-select: none; }
93
+ .disp-legend { display: flex; flex-direction: column; gap: 6px; color: var(--muted); }
94
+ .disp-legend > span { white-space: nowrap; }
95
+ .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; }
96
+ .disp-legend .sw { margin-right: 6px; }
97
+ .sw.res { border-radius: 2px; }
98
+ .disp-row .lkey, .disp-legend .lkey { display: inline-block; width: 22px; border-top: 2px solid var(--edge); vertical-align: 3px; }
99
+ .disp-legend .lkey { margin-right: 6px; }
100
+ .lkey.dot { border-top-style: dotted; } .lkey.dash { border-top-style: dashed; }
77
101
  main { flex: 1 1 auto; display: flex; min-height: 0; position: relative; }
78
102
  #graph { flex: 1 1 auto; min-width: 0; cursor: grab; touch-action: none; }
79
103
  #graph:active { cursor: grabbing; }
@@ -117,9 +141,27 @@
117
141
  /* ---- Mobile: on narrow screens, render the panel as a bottom sheet ---- */
118
142
  @media (max-width: 640px) {
119
143
  body { height: 100dvh; }
120
- .toolbar { column-gap: 12px; padding: 8px 12px; font-size: 12px; }
144
+ .toolbar { column-gap: 10px; padding: 8px 12px; font-size: 12px; flex-wrap: nowrap; }
121
145
  .brand .sub { display: none; }
122
- .legend { width: 100%; margin-left: 0; justify-content: flex-start; font-size: 11px; }
146
+ .counts-full { display: none; }
147
+ .counts-min { display: inline; }
148
+ .display-label { display: none; } /* Display collapses to icon-only */
149
+ .display-btn { width: 32px; padding: 0; }
150
+ /* Display popover → bottom sheet, mirroring the #panel idiom */
151
+ .display-menu.open .display-backdrop { display: block; position: fixed; inset: 0; background: rgba(0, 0, 0, 0.35); z-index: 40; }
152
+ .display-pop {
153
+ position: fixed; left: 0; right: 0; bottom: 0; top: auto;
154
+ min-width: 0; width: 100%;
155
+ border-radius: 16px 16px 0 0; border-left: none; border-right: none; border-bottom: none;
156
+ box-shadow: 0 -8px 24px rgba(0, 0, 0, 0.45);
157
+ padding: 26px 18px 22px; z-index: 41;
158
+ transform: translateY(101%); transition: transform 0.24s ease;
159
+ display: block; /* always present; slid off-screen until open */
160
+ }
161
+ .display-menu.open .display-pop { transform: translateY(0); }
162
+ .display-pop::before { content: ""; position: absolute; top: 9px; left: 50%; width: 40px; height: 4px; margin-left: -20px; background: var(--line); border-radius: 2px; }
163
+ .disp-row { padding: 9px 0; font-size: 14px; }
164
+ .disp-legend { font-size: 13px; gap: 8px; }
123
165
  #panel {
124
166
  position: absolute;
125
167
  left: 0; right: 0; bottom: 0;
@@ -156,20 +198,28 @@
156
198
  <body>
157
199
  <header class="toolbar">
158
200
  <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>
201
+ <div class="counts" id="counts"><span class="counts-full"></span><span class="counts-min"></span></div>
170
202
  <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>
203
+ <div class="display-menu" id="display-menu">
204
+ <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>
205
+ <div class="display-backdrop" id="display-backdrop"></div>
206
+ <div class="display-pop" id="display-pop" role="menu">
207
+ <h3>Show</h3>
208
+ <label class="disp-row"><input type="checkbox" id="t-trans" checked /><span class="lkey"></span>Navigate</label>
209
+ <label class="disp-row"><input type="checkbox" id="t-calls" checked /><span class="lkey dash"></span>Call</label>
210
+ <h3 class="grp">Legend</h3>
211
+ <div class="disp-legend">
212
+ <span style="font-weight:700">▶ Start</span>
213
+ <span><span class="sw"></span>Screen</span>
214
+ <span><span class="sw res"></span>Resource</span>
215
+ <span><span class="lkey"></span>Navigate</span>
216
+ <span><span class="lkey dot"></span>Contains</span>
217
+ <span><span class="lkey dash"></span>Call</span>
218
+ </div>
219
+ </div>
220
+ </div>
221
+ <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>
222
+ <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
223
  </div>
174
224
  </header>
175
225
  <main>
@@ -264,10 +314,14 @@
264
314
  {
265
315
  const navCount = MAP.transitions.filter((t) => (t.kind || "navigate") === "navigate").length;
266
316
  const containCount = MAP.transitions.length - navCount;
267
- document.getElementById("counts").textContent =
317
+ const countsEl = document.getElementById("counts");
318
+ countsEl.querySelector(".counts-full").textContent =
268
319
  MAP.screens.length + " screens · " + MAP.resources.length + " resources · " +
269
320
  navCount + " navigate" + (containCount ? " · " + containCount + " contains" : "") +
270
321
  " · " + MAP.calls.length + " calls";
322
+ // compact form for the mobile toolbar: "12S 8R"
323
+ countsEl.querySelector(".counts-min").innerHTML =
324
+ MAP.screens.length + "<i>S</i> " + MAP.resources.length + "<i>R</i>";
271
325
  }
272
326
 
273
327
  // ====== force-directed layout (reference approach) ======
@@ -497,6 +551,21 @@
497
551
  // ---- fit-to-view button ----
498
552
  document.getElementById("fit-btn").addEventListener("click", fitToView);
499
553
 
554
+ // ---- Display menu (edge filters + legend): dropdown on desktop, bottom sheet on mobile ----
555
+ {
556
+ const menu = document.getElementById("display-menu");
557
+ const btn = document.getElementById("display-btn");
558
+ const close = () => { menu.classList.remove("open"); btn.setAttribute("aria-expanded", "false"); };
559
+ btn.addEventListener("click", (e) => {
560
+ e.stopPropagation();
561
+ const open = menu.classList.toggle("open");
562
+ btn.setAttribute("aria-expanded", open ? "true" : "false");
563
+ });
564
+ document.getElementById("display-backdrop").addEventListener("click", close);
565
+ document.addEventListener("click", (e) => { if (!menu.contains(e.target)) close(); });
566
+ document.addEventListener("keydown", (e) => { if (e.key === "Escape") close(); });
567
+ }
568
+
500
569
  // ---- panel ----
501
570
  const panel = document.getElementById("panel-body"); // content container (close button/handle are preserved)
502
571
  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.17",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },