Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions graphcode-windows/build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ pub fn build(b: *std.Build) !void {
.file = b.path("src/FolderPicker.c"),
.flags = &.{ "-DUNICODE", "-D_UNICODE" },
});
exe.addCSourceFile(.{
.file = b.path("src/FilePicker.c"),
.flags = &.{ "-DUNICODE", "-D_UNICODE" },
});
exe.addCSourceFile(.{
.file = b.path("src/AccessibilityProvider.cpp"),
.flags = &.{ "-Wno-unused-command-line-argument" },
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"id":"22222222-2222-4222-8222-222222222222","title":"Loop","loopType":"turnBased","checkDescription":"check","triggerPrompt":null,"firstInstruction":"look at [image #1]","pausesBeforeWritesOnly":false,"goal":null,"backend":null,"modelTier":null,"worktree":null,"subGraph":null,"createdBy":null,"attachments":[{"id":"aaaaaaaa-1111-4111-8111-111111111111","path":"C:\\Users\\me\\.graphcode\\memory\\my-project\\22222222-2222-4222-8222-222222222222\\attachments\\attachment-1.png"}]}
65 changes: 53 additions & 12 deletions graphcode-windows/src/App.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1136,6 +1136,12 @@ pub const App = struct {
const path = self.allocator.dupe(u8, current_path) catch return;
defer self.allocator.free(path);
const settings = self.product_settings orelse return;
// Generated before the dialog opens (rather than at send time, as every other
// draft field is) so a file picked mid-dialog can be copied straight into the
// attachments directory this node will end up owning, instead of a temporary
// location that would need a second copy once the real id is known.
var draft_id_buffer: [36]u8 = undefined;
Forms.generateDraftId(&draft_id_buffer);
const initial = Forms.NodeDraft{
.title = "",
.backend = settings.default_backend,
Expand All @@ -1151,15 +1157,11 @@ pub const App = struct {
};
defer templates.deinit();
if (templates.templates.items.len == 0) {
var draft = NativeForms.node(self.window.hwnd, self.allocator, initial) catch {
self.setStatus("Unable to open node form");
var draft = NativeForms.node(self.window.hwnd, self.allocator, path, &draft_id_buffer, initial) catch |err| {
self.setStatus(nodeFormErrorStatus(err));
return;
} orelse return;
defer draft.deinit(self.allocator);
Forms.validateNode(draft) catch {
self.setStatus("Invalid node form");
return;
};
self.client.sendCreateNodeDraft(path, draft);
return;
}
Expand All @@ -1185,19 +1187,15 @@ pub const App = struct {
var owns_current = false;
defer if (owns_current) current.deinit(self.allocator);
while (true) {
const result = NativeForms.nodeWithTemplates(self.window.hwnd, self.allocator, current, true) catch {
self.setStatus("Unable to open node form");
const result = NativeForms.nodeWithTemplates(self.window.hwnd, self.allocator, path, &draft_id_buffer, current, true) catch |err| {
self.setStatus(nodeFormErrorStatus(err));
return;
};
switch (result) {
.cancelled => return,
.draft => |draft| {
var submitted = draft;
defer submitted.deinit(self.allocator);
Forms.validateNode(submitted) catch {
self.setStatus("Invalid node form");
return;
};
self.client.sendCreateNodeDraft(path, submitted);
return;
},
Expand All @@ -1218,6 +1216,34 @@ pub const App = struct {
}
}

fn nodeFormErrorStatus(err: anyerror) []const u8 {
return switch (err) {
error.EmptyTitle,
error.MissingSource,
error.MissingTarget,
error.SameEndpoint,
error.UnsupportedLoopType,
error.UnsupportedEdgeKind,
error.UnsupportedEdgeCondition,
error.UnsupportedTransform,
error.UnsupportedBackend,
error.UnsupportedModelTier,
error.UnsupportedMetricDirection,
error.InvalidGoal,
error.InvalidWorktree,
error.InvalidSubgraph,
error.InvalidCreatedBy,
error.InvalidCycleGuard,
error.InvalidNumericInput,
error.MissingFirstInstruction,
error.MissingTriggerPrompt,
error.EmptyJumpQuery,
error.TooManyAttachments,
=> "Invalid node form",
else => "Unable to open node form",
};
}

fn editSelectedNode(self: *App) void {
const graph = self.model.graph orelse return;
const index = self.model.selectedIndex() orelse return;
Expand Down Expand Up @@ -5782,6 +5808,21 @@ test "jump matching ranks exact results across projects" {
try std.testing.expectEqual(@as(u8, 2), prefix.score);
}

test "node form validation errors keep the validation status" {
try std.testing.expectEqualStrings(
"Invalid node form",
App.nodeFormErrorStatus(error.MissingFirstInstruction),
);
try std.testing.expectEqualStrings(
"Invalid node form",
App.nodeFormErrorStatus(error.TooManyAttachments),
);
try std.testing.expectEqualStrings(
"Unable to open node form",
App.nodeFormErrorStatus(error.FormCreationFailed),
);
}

fn runSmokeWorkspaceActions(self: *App) void {
const script = self.smoke_workspace_actions;
if (script.len == 0) return;
Expand Down
22 changes: 15 additions & 7 deletions graphcode-windows/src/DaemonClient.zig
Original file line number Diff line number Diff line change
Expand Up @@ -363,13 +363,21 @@ pub const DaemonClient = struct {
project_path: []const u8,
draft: Forms.NodeDraft,
) void {
var node_id: [36]u8 = undefined;
self.mutex.lock();
const sequence = self.next_draft;
self.next_draft +%= 1;
self.mutex.unlock();
makeRequestID(&node_id, sequence);
const command = Wire.commandGraphCreateNodeFull(self.allocator, project_path, &node_id, draft) catch {
var generated_id: [36]u8 = undefined;
// A draft that ingested an attachment already chose its id before the dialog
// opened (`Forms.generateDraftId`, threaded through `NativeForms.node`) — the
// attachment bytes are already filed under that id on disk, so the node this
// creates has to carry the same one. Everything else keeps the historical
// generate-at-send-time id.
const node_id: []const u8 = if (draft.node_id.len != 0) draft.node_id else blk: {
self.mutex.lock();
const sequence = self.next_draft;
self.next_draft +%= 1;
self.mutex.unlock();
makeRequestID(&generated_id, sequence);
break :blk &generated_id;
};
const command = Wire.commandGraphCreateNodeFull(self.allocator, project_path, node_id, draft) catch {
self.publishState(self.connectionState(), "create node command encoding failed");
return;
};
Expand Down
Loading
Loading