From c895d99860c487119b0aae9da9f603df8622166f Mon Sep 17 00:00:00 2001 From: "fizzy-linux[bot]" <346940526+fizzy-linux[bot]@users.noreply.github.com> Date: Tue, 27 Oct 2026 03:09:06 -0500 Subject: [PATCH] build: tape and replay in the build-API fixture plugin Part of fizzyedit/work#326 Agent-Team: linux Worker: claude Co-Authored-By: Claude Sonnet 5.5 --- scripts/fixtures/build-api/plugin/plugin.zig | 27 ++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/scripts/fixtures/build-api/plugin/plugin.zig b/scripts/fixtures/build-api/plugin/plugin.zig index 3e578d97f..dffb3e589 100644 --- a/scripts/fixtures/build-api/plugin/plugin.zig +++ b/scripts/fixtures/build-api/plugin/plugin.zig @@ -4,6 +4,9 @@ const std = @import("std"); const builtin = @import("builtin"); const sdk = @import("fizzy_sdk"); const dvui = @import("dvui"); +const core = @import("core"); +const tape = @import("tape"); +const replay = @import("replay"); const fixture_lib = @import("fixture_lib"); pub const plugin_options = @import("fizzy_plugin_options"); @@ -19,6 +22,12 @@ comptime { } } +// `tape` and `replay` are `core`'s own instances (design/build-api.md): the anchors a plugin +// publishes through `core` are the very ones `replay` reads, so one file type serves both. +comptime { + if (core.anchor != replay.anchor) @compileError("replay is not core's instance"); +} + // The host writes plugin lifecycle state on the main UI thread. var plugin: sdk.Plugin = .{ .state = undefined, @@ -32,6 +41,7 @@ const vtable: sdk.Plugin.VTable = .{}; pub fn register(host: *sdk.Host) !void { // Calls into the C source of the package's own dependency: it must have reached the link. if (fixture_lib.fixture_answer() != 42) return error.FixtureDependency; + try writeAndLoadTape(host.allocator); try host.registerPlugin(&plugin); try host.registerSurface(.{ .id = plugin_id ++ ".surface", @@ -47,3 +57,20 @@ fn draw(_: ?*anyopaque) !dvui.App.Result { dvui.label(@src(), "{s}", .{plugin_id}, .{}); return .ok; } + +/// A plugin writes a demo with `tape.Script`, ships it as bytes and loads it back: the tape +/// library a plugin imports is the one the host plays with. +fn writeAndLoadTape(gpa: std.mem.Allocator) !void { + var owned = owned: { + var script: tape.Script = .init(gpa, plugin_id, plugin_options.name); + errdefer script.deinit(); + try script.command(plugin_id ++ ".hello"); + break :owned try script.finish(); + }; + defer owned.deinit(); + const bytes = try tape.binary.encode(gpa, owned.tape, .{}); + defer gpa.free(bytes); + var loaded = try tape.Tape.load(gpa, bytes, .{}); + defer loaded.deinit(); + if (!@hasDecl(replay.Player, "play")) return error.FixtureReplay; +}