Skip to content

Commit e45ca49

Browse files
committed
Make store not use paned widget, tabs instead for clarity
1 parent 8dbe4d1 commit e45ca49

5 files changed

Lines changed: 56 additions & 106 deletions

File tree

VERSION

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
0.1.1
1+
0.1.2

assets/macos/info.plist

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@
99
<key>CFBundleIdentifier</key>
1010
<string>com.foxnne.fizzy</string>
1111
<key>CFBundleShortVersionString</key>
12-
<string>0.1.1</string>
12+
<string>0.1.2</string>
1313
<key>CFBundleVersion</key>
14-
<string>0.1.1</string>
14+
<string>0.1.2</string>
1515
<key>CFBundleIconFile</key>
1616
<string>fizzy.icns</string>
1717
<!--

build.zig.zon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
},
1212
.fingerprint = 0x3311781b569a8e73,
1313
.name = .fizzy,
14-
.version = "0.1.1",
14+
.version = "0.1.2",
1515
.minimum_zig_version = "0.16.0",
1616
.dependencies = .{
1717
.zig_objc = .{

src/editor/PluginStore.zig

Lines changed: 52 additions & 97 deletions
Original file line numberDiff line numberDiff line change
@@ -64,14 +64,8 @@ var catalog: ?store.Catalog = null;
6464
var registry_url_owned: ?[]u8 = null;
6565
var first_draw_done = false;
6666

67-
/// Upper/lower split (installed plugins on top, store on bottom) — same shape and autosizing
68-
/// behaviour as the Pixi tools pane (`explorer/tools.zig`'s layers/palettes split): the top
69-
/// pane's height autofits to its content every frame, and a manual drag becomes the new ceiling
70-
/// so autofit never grows back past a size the user deliberately chose. The split ratio itself
71-
/// is owned internally by the `PanedWidget` (persisted via `dvui.data`); only the ceiling and
72-
/// the previous shown-count (to detect when a refit is needed) are ours to track. See `draw`.
73-
var installed_max_split_ratio: f32 = 0.5;
74-
var prev_installed_shown: usize = 0;
67+
const PaneTab = enum { store, installed };
68+
var selected_pane_tab: PaneTab = .store;
7569

7670
var store_scroll_info: dvui.ScrollInfo = .{ .horizontal = .auto };
7771
var installed_scroll_info: dvui.ScrollInfo = .{ .horizontal = .auto };
@@ -647,15 +641,15 @@ fn drawCreditLink(src: std.builtin.SourceLocation, text: []const u8, url: ?[]con
647641
fn drawDetailTabs() void {
648642
var strip = dvui.box(@src(), .{ .dir = .horizontal }, .{
649643
.expand = .horizontal,
650-
.padding = .{ .x = 12 },
644+
.padding = .{ .x = 12, .y = 6, .h = 12 },
651645
});
652646
defer strip.deinit();
653647

654-
drawDetailTab(@src(), "DETAILS", 0, selected_detail_tab == .details);
655-
drawDetailTab(@src(), "CHANGELOG", 1, selected_detail_tab == .changelog);
648+
if (tabButton(@src(), "DETAILS", 0, selected_detail_tab == .details)) selected_detail_tab = .details;
649+
if (tabButton(@src(), "CHANGELOG", 1, selected_detail_tab == .changelog)) selected_detail_tab = .changelog;
656650
}
657651

658-
fn drawDetailTab(src: std.builtin.SourceLocation, label: []const u8, id_extra: usize, selected: bool) void {
652+
fn tabButton(src: std.builtin.SourceLocation, label: []const u8, id_extra: usize, selected: bool) bool {
659653
const theme = dvui.themeGet();
660654

661655
// Sized to the label's own width (no expand), so the underline drawn below — which does
@@ -669,15 +663,11 @@ fn drawDetailTab(src: std.builtin.SourceLocation, label: []const u8, id_extra: u
669663
.id_extra = id_extra,
670664
.background = false,
671665
.border = dvui.Rect.all(0),
672-
.margin = dvui.Rect.all(0),
666+
.margin = dvui.Rect.all(2),
673667
.padding = .{ .x = 4, .y = 6, .w = 4, .h = 4 },
674668
.color_text = if (selected) theme.color(.window, .text) else theme.color(.control, .text),
675-
.font = tab_font.withSize(tab_font.size - 1),
669+
.font = tab_font.withSize(tab_font.size),
676670
});
677-
if (clicked) {
678-
selected_detail_tab = if (id_extra == 0) .details else .changelog;
679-
}
680-
681671
var underline = dvui.box(@src(), .{}, .{
682672
.id_extra = id_extra,
683673
.expand = .horizontal,
@@ -686,6 +676,8 @@ fn drawDetailTab(src: std.builtin.SourceLocation, label: []const u8, id_extra: u
686676
.color_fill = if (selected) theme.color(.window, .text) else .transparent,
687677
});
688678
underline.deinit();
679+
680+
return clicked;
689681
}
690682

691683
/// CHANGELOG's content until real GitHub Releases fetching lands (tracked separately) — an
@@ -1254,8 +1246,8 @@ fn rankEntries(entries: *std.ArrayListUnmanaged(StoreEntry), query: *const fuzzy
12541246

12551247
fn draw(_: ?*anyopaque) anyerror!void {
12561248
// Unlike the old flat list, the tab now fills the full explorer viewport height
1257-
// (`expand = .both`, not just `.horizontal`) so the upper/lower paned split below gets a
1258-
// genuinely bounded height to divide — each pane then scrolls its own overflow (see
1249+
// (`expand = .both`, not just `.horizontal`) so the section below the tab strip gets a
1250+
// genuinely bounded height — it then scrolls its own overflow (see
12591251
// `drawStoreSection`/`drawInstalledSection`) rather than the whole tab growing forever and
12601252
// leaning on the explorer's own scrollArea (`files.zig`'s tree still does that; we don't).
12611253
var vbox = dvui.box(@src(), .{ .dir = .vertical }, .{ .expand = .both });
@@ -1303,7 +1295,7 @@ fn draw(_: ?*anyopaque) anyerror!void {
13031295
const arena = dvui.currentWindow().arena();
13041296
const editor = fizzy.editor;
13051297

1306-
// Store entries (upper pane): one row per registry plugin, independent of local install
1298+
// Store entries (STORE tab): one row per registry plugin, independent of local install
13071299
// state — a pure "what does the store publish" list. See `drawStoreCard`.
13081300
var store_entries: std.ArrayListUnmanaged(StoreEntry) = .empty;
13091301
if (maybe_snapshot) |snap| {
@@ -1320,7 +1312,7 @@ fn draw(_: ?*anyopaque) anyerror!void {
13201312
}
13211313
rankEntries(&store_entries, &query);
13221314

1323-
// Installed entries (lower pane): everything genuinely present locally — loaded,
1315+
// Installed entries (INSTALLED tab): everything genuinely present locally — loaded,
13241316
// disabled-on-disk, sideloaded, or a failed/rejected build — enriched with a matching
13251317
// registry row (for "store vX" / Update-availability) wherever the registry knows the id too.
13261318
var installed_entries: std.ArrayListUnmanaged(StoreEntry) = .empty;
@@ -1381,55 +1373,36 @@ fn draw(_: ?*anyopaque) anyerror!void {
13811373
}
13821374
rankEntries(&installed_entries, &query);
13831375

1384-
// Upper/lower split — identical shape and autosizing behaviour to the Pixi tools pane's
1385-
// layers/palettes split (`explorer/tools.zig`): the installed pane autofits snugly to its
1386-
// content every frame, up to `installed_max_split_ratio`, unless the sash is actively being
1387-
// dragged or an animation is in flight — a manual drag becomes the new ceiling so autofit
1388-
// never grows back past a size the user deliberately chose.
1389-
var paned = fizzy.dvui.paned(@src(), .{
1390-
.direction = .vertical,
1391-
.collapsed_size = 0,
1392-
.handle_size = 10,
1393-
// Same reveal distance as every other sash in the app; the default 20 made this one
1394-
// appear much later than its neighbours for no reason.
1395-
.handle_dynamic = .{ .handle_size_max = 10, .distance_max = 60 },
1396-
}, .{ .expand = .both, .background = false });
1397-
defer paned.deinit();
1398-
1399-
if (paned.dragging) installed_max_split_ratio = paned.split_ratio.*;
1400-
1401-
var shown_installed: usize = 0;
1402-
if (paned.showFirst()) {
1403-
shown_installed = drawInstalledSection(installed_entries.items, filter_text);
1404-
}
1405-
1406-
// Must run between `showFirst` and `showSecond` — `getFirstFittedRatio` reads the min size
1407-
// the first pane's just-drawn content published.
1408-
const autofit = !paned.dragging and !paned.animating;
1409-
if (dvui.firstFrame(paned.data().id) or prev_installed_shown != shown_installed or autofit) {
1410-
if (dvui.firstFrame(paned.data().id)) {
1411-
// Min sizes for the subtree aren't published yet on the very first frame — so a fit
1412-
// computed right now would be wrong. Nudge open (never hard-close to exactly 0):
1413-
// `showFirst` below gates whether the installed pane's content runs *at all*, so a
1414-
// 0 here would deadlock — the pane could never publish a size to refit from again,
1415-
// and only a manual drag of the sash would ever reopen it. Refit properly next frame.
1416-
paned.split_ratio.* = 1.0;
1417-
} else {
1418-
const ratio = paned.getFirstFittedRatio(.{
1419-
.min_split = 0,
1420-
.max_split = @min(installed_max_split_ratio, 0.6),
1421-
.min_size = 0,
1422-
});
1423-
const diff = @abs(ratio - paned.split_ratio.*);
1424-
if (diff > 0.000001) {
1425-
paned.animateSplit(ratio, dvui.easing.outBack);
1426-
}
1427-
}
1376+
drawPaneTabs(cat.status());
1377+
1378+
switch (selected_pane_tab) {
1379+
.store => _ = drawStoreSection(store_entries.items, filter_text, cat.status()),
1380+
.installed => _ = drawInstalledSection(installed_entries.items, filter_text),
14281381
}
1429-
prev_installed_shown = shown_installed;
1382+
}
1383+
1384+
/// Store / Installed strip below the filter row. Same look as the detail page's
1385+
/// DETAILS/CHANGELOG strip (`drawDetailTabs`), plus the store's refresh spinner pinned to the
1386+
/// right so an in-flight fetch stays visible from either tab.
1387+
fn drawPaneTabs(status: store.Status) void {
1388+
var strip = dvui.box(@src(), .{ .dir = .horizontal }, .{
1389+
.expand = .horizontal,
1390+
.padding = .{ .x = 12, .y = 6, .h = 6 },
1391+
});
1392+
defer strip.deinit();
1393+
1394+
if (tabButton(@src(), "STORE", 0, selected_pane_tab == .store)) selected_pane_tab = .store;
1395+
if (tabButton(@src(), "INSTALLED", 1, selected_pane_tab == .installed)) selected_pane_tab = .installed;
14301396

1431-
if (paned.showSecond()) {
1432-
_ = drawStoreSection(store_entries.items, filter_text, cat.status());
1397+
// A refresh over existing cards is a footnote, not a takeover: the cards stay put and this
1398+
// spinner is the only sign a fetch is outstanding.
1399+
if (status == .fetching and have_snapshot) {
1400+
fizzy.dvui.bubbleSpinner(@src(), .{
1401+
.min_size_content = .{ .w = 14, .h = 14 },
1402+
.gravity_x = 1.0,
1403+
.gravity_y = 0.5,
1404+
.color_text = dvui.themeGet().color(.window, .text).opacity(0.7),
1405+
}, .{});
14331406
}
14341407
}
14351408

@@ -1536,31 +1509,15 @@ fn cardAnimator(src: std.builtin.SourceLocation, entry: StoreEntry, index: usize
15361509
});
15371510
}
15381511

1539-
/// Lower pane: a pure "browse the store" list, one card per registry plugin. Wrapped in its
1512+
/// STORE tab: a pure "browse the store" list, one card per registry plugin. Wrapped in its
15401513
/// own scrollArea (independent of the installed pane above) with the same edge-shadow treatment
1541-
/// `explorer/Explorer.zig` uses, so a manually-shrunk pane or an overly-wide card still scrolls
1542-
/// with the usual visual hint instead of clipping silently. Returns the shown count (unused by
1543-
/// the caller now that the store pane no longer drives the paned autofit).
1514+
/// `explorer/Explorer.zig` uses, so a narrow pane or an overly-wide card still scrolls with the
1515+
/// usual visual hint instead of clipping silently. Returns the shown count (unused).
15441516
///
15451517
/// This pane — and only this pane — owns the registry's loading/offline states: the spinner while
15461518
/// the first fetch is in flight, the unreachable empty state when it fails, and a small inline
15471519
/// spinner beside the header while a refresh runs over already-shown cards.
15481520
fn drawStoreSection(entries: []const StoreEntry, filter_text: []const u8, status: store.Status) usize {
1549-
{
1550-
var header = dvui.box(@src(), .{ .dir = .horizontal }, .{ .expand = .horizontal });
1551-
defer header.deinit();
1552-
dvui.labelNoFmt(@src(), "STORE", .{}, .{ .font = dvui.Font.theme(.heading), .margin = .{ .x = 8 } });
1553-
// A refresh over existing cards is a footnote, not a takeover: the cards stay put and this
1554-
// spinner is the only sign a fetch is outstanding.
1555-
if (status == .fetching and have_snapshot) {
1556-
fizzy.dvui.bubbleSpinner(@src(), .{
1557-
.min_size_content = .{ .w = 14, .h = 14 },
1558-
.gravity_y = 0.5,
1559-
.color_text = dvui.themeGet().color(.window, .text).opacity(0.7),
1560-
}, .{});
1561-
}
1562-
}
1563-
15641521
var pane_box = dvui.box(@src(), .{ .dir = .vertical }, .{ .expand = .both, .background = false });
15651522
defer pane_box.deinit();
15661523

@@ -1605,13 +1562,11 @@ fn drawStoreSection(entries: []const StoreEntry, filter_text: []const u8, status
16051562
return shown;
16061563
}
16071564

1608-
/// Upper pane: everything genuinely present locally, grouped under "Local" (sideloaded dylibs)
1565+
/// INSTALLED tab: everything genuinely present locally, grouped under "Local" (sideloaded dylibs)
16091566
/// and "Built-in" (bundled + static built-ins) headers. This is the *only* place enable/disable,
16101567
/// update, uninstall, and failed-to-load detail show up — see `drawCard`. Returns the shown
1611-
/// count (drives the paned autofit refit trigger in `draw`).
1568+
/// count (unused).
16121569
fn drawInstalledSection(entries: []const StoreEntry, filter_text: []const u8) usize {
1613-
dvui.labelNoFmt(@src(), "INSTALLED", .{}, .{ .font = dvui.Font.theme(.heading), .margin = .{ .y = 4 } });
1614-
16151570
var pane_box = dvui.box(@src(), .{ .dir = .vertical }, .{ .expand = .both, .background = false });
16161571
defer pane_box.deinit();
16171572

@@ -1662,7 +1617,7 @@ fn drawInstalledSection(entries: []const StoreEntry, filter_text: []const u8) us
16621617
return shown;
16631618
}
16641619

1665-
/// Small uppercase-ish section label above a group of cards in the upper pane. `id_extra`
1620+
/// Small uppercase-ish section label above a group of cards in the INSTALLED tab. `id_extra`
16661621
/// disambiguates the "Local" and "Built-in" calls, which otherwise share a source location.
16671622
fn drawSectionHeader(title: []const u8, id_extra: usize) void {
16681623
dvui.labelNoFmt(@src(), title, .{}, .{
@@ -1678,15 +1633,15 @@ fn isBuiltIn(id: []const u8) bool {
16781633
return isBundled(id);
16791634
}
16801635

1681-
/// Lower-pane card: full state — enabled checkbox, update/uninstall, failed-to-load detail —
1636+
/// INSTALLED-tab card: full state — enabled checkbox, update/uninstall, failed-to-load detail —
16821637
/// via `drawCardControls`/`infoLine` (which still shows "installed vX"). See `drawCardShell`.
16831638
fn drawCard(entry: StoreEntry) void {
16841639
var buf: [192]u8 = undefined;
16851640
drawCardShell(entry, drawCardControls, infoLine(&buf, entry), true);
16861641
}
16871642

1688-
/// Upper-pane card: browse-only — just an install button or a "no compatible build" message via
1689-
/// `drawStoreCardControls`/`storeInfoLine` (never "installed vX": that's the lower pane's job,
1643+
/// STORE-tab card: browse-only — just an install button or a "no compatible build" message via
1644+
/// `drawStoreCardControls`/`storeInfoLine` (never "installed vX": that's the INSTALLED tab's job,
16901645
/// even for a store plugin the user happens to already have installed). See `drawCardShell`.
16911646
fn drawStoreCard(entry: StoreEntry) void {
16921647
var buf: [192]u8 = undefined;
@@ -2010,7 +1965,7 @@ fn infoLine(buf: []u8, entry: StoreEntry) []const u8 {
20101965
}
20111966

20121967
/// Compose the dim `id · store v{latest}` line for a store (upper-pane) card into `buf`. Unlike
2013-
/// `infoLine`, this never shows install state — the upper pane is a pure "what does the store
1968+
/// `infoLine`, this never shows install state — the STORE tab is a pure "what does the store
20141969
/// publish" list, even for a plugin the user happens to already have installed (see `drawCard`).
20151970
fn storeInfoLine(buf: []u8, entry: StoreEntry) []const u8 {
20161971
var latest_buf: [40]u8 = undefined;

src/plugins/markdown/src/markdown.zig

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,6 @@ pub const Preview = struct {
156156
if (self.ast_root == null and self.parse_job == null) {
157157
self.content_hash = h;
158158
self.rs.clear(gpa);
159-
dvui.log.info("markdown: async parse start ({d} bytes)", .{content.len});
160159
self.startParseJob(content, gpa, h);
161160
return;
162161
}
@@ -246,10 +245,6 @@ pub const Preview = struct {
246245
self.rs.deinit(gpa);
247246
self.rs = job.rs;
248247
job.rs = .{};
249-
dvui.log.info("markdown: async parse ready ({d} bytes, {d} top-level blocks)", .{
250-
job.bytes.len,
251-
self.rs.blocks.len(),
252-
});
253248
job.destroy();
254249
self.parse_job = null;
255250
dvui.refresh(null, @src(), null);

0 commit comments

Comments
 (0)