summaryrefslogtreecommitdiff
path: root/src/pardes.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-08-10 09:58:31 -0300
committerGabriel Schneider <[email protected]>2026-08-11 09:58:59 -0300
commiteb11ab331b4e13e2b9e5a673a4c012d22fdd1d9c (patch)
treecc993ad239451adb6f23645ee5ca001802832ed0 /src/pardes.zig
parent38e9919a9ea9055538409b388d580c4e4c838434 (diff)
downloadpardes-eb11ab331b4e13e2b9e5a673a4c012d22fdd1d9c.tar.gz
pardes-eb11ab331b4e13e2b9e5a673a4c012d22fdd1d9c.zip
macos: the AppKit shell, its icon, and the offscreen e2e harness
Diffstat (limited to 'src/pardes.zig')
-rw-r--r--src/pardes.zig48
1 files changed, 48 insertions, 0 deletions
diff --git a/src/pardes.zig b/src/pardes.zig
index 4ce4d101..ec938a6d 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -45,6 +45,12 @@ pub const lsp = @import("lsp/lsp.zig");
pub const Platform = enum { tty, gui, web, macos };
pub const platform: Platform = @field(Platform, @tagName(@import("pardes_config").platform));
+/// Frontends that draw their own text, and can therefore be told which face to
+/// wear. On the tty the font belongs to the terminal emulator and in the
+/// browser it belongs to the page, so there the Font builtins are not
+/// disabled so much as meaningless — see builtins.zig.
+pub const font_picker = platform == .gui or platform == .macos;
+
/// Native PDF quality is a shell property, but the core owns MuPDF and the
/// RGBA cache. Kitty favors wire bandwidth; SDL favors physical-pixel text
/// quality and asks the renderer to cover either fit axis without upscaling.
@@ -4199,6 +4205,18 @@ const PendingPipe = struct {
}
};
+/// The acme verb the core just performed, for a shell that can answer with
+/// something physical. macOS taps the trackpad under the finger that asked
+/// (NSHapticFeedbackManager); the SDL shell already does the same thing with a
+/// gamepad — `rumble` in src/gui/deck.zig, "a brief gentle ack for
+/// execute/look, not a buzz". Two verbs rather than one flag because they
+/// deserve to feel different: Exec did something, Look went somewhere.
+pub const Haptic = enum { none, exec, look };
+
+/// Zero-sized off macOS, the way PdfSlot is off -Dmupdf: no other shell reads
+/// the field, so no other shell carries it.
+const HapticSlot = if (platform == .macos) Haptic else void;
+
pub const Pardes = struct {
gpa: std.mem.Allocator,
image_gpa: std.mem.Allocator,
@@ -4273,6 +4291,10 @@ pub const Pardes = struct {
/// the PETSCII matcher without it.
native_images: bool = false,
quit: bool = false,
+ /// The Look or Exec that has happened and not yet been felt, taken by the
+ /// shell once per pump (takeHaptic). A pulse, not a queue: five Execs
+ /// inside one keystroke are still one thing the hand did.
+ haptic: HapticSlot = if (platform == .macos) .none else {},
drag: Drag = .none,
hover_col: u16 = 0,
hover_row: u16 = 0,
@@ -4431,6 +4453,9 @@ pub const Pardes = struct {
p.applyStartupConfig();
p.finishThemeInitialization();
p.sync();
+ // A config file that opens a file with `Look …` armed the pulse before
+ // anyone touched anything. Nobody asked for that, so boot is silent.
+ _ = p.takeHaptic();
return p;
}
@@ -11603,6 +11628,7 @@ pub const Pardes = struct {
/// search of the pane it came from, which is acme's button-3.
pub fn lookAt(p: *Pardes, id: usize, txt: []const u8) void {
const pane = p.panes[id] orelse return;
+ p.noteHaptic(.look);
const trimmed = std.mem.trim(u8, txt, " \t\r\n");
// `` @`ls -la` `` names a COMMAND, not a path: run it, and land in the
// pane that answers — looking at a thing means being SHOWN it, and a
@@ -11770,6 +11796,10 @@ pub const Pardes = struct {
const pane = p.panes[id] orelse return null;
const cmd = commandText(txt);
if (cmd.len == 0) return null;
+ // Before the builtin dispatch, and only at depth zero: `Exec ls` comes
+ // back through here as `ls` (executeBuiltinLine holds the depth), and
+ // one Tab is one thing the hand did, however many words it unwraps to.
+ if (p.exec_depth == 0) p.noteHaptic(.exec);
if (p.executeBuiltinLine(id, cmd)) return null;
if (p.exec_depth >= max_exec_depth) return null;
p.exec_depth += 1;
@@ -12244,6 +12274,7 @@ pub const Pardes = struct {
p.applyStartupConfig();
p.finishThemeInitialization();
p.sync();
+ _ = p.takeHaptic(); // see init: a restored session is not a gesture
return p;
}
@@ -12511,6 +12542,23 @@ pub const Pardes = struct {
return p.chrome_animation.isActive();
}
+ /// Arm the pulse. Look wins a tie because a Look that runs a command
+ /// (`` @`ls` ``, which is one gesture spelled as both) is felt as the
+ /// thing the user asked for, not as the shell it happened to need.
+ fn noteHaptic(p: *Pardes, pulse: Haptic) void {
+ if (comptime platform != .macos) return;
+ if (p.haptic == .look) return;
+ p.haptic = pulse;
+ }
+
+ /// Take the armed pulse and disarm. The shell calls this once per pump,
+ /// after the tick that may have set it.
+ pub fn takeHaptic(p: *Pardes) Haptic {
+ if (comptime platform != .macos) return .none;
+ defer p.haptic = .none;
+ return p.haptic;
+ }
+
fn finishThemeInitialization(p: *Pardes) void {
p.chrome_animation.snap(ChromeTheme.fromTheme(p.theme()));
p.animate_theme_changes = true;