mirror of
https://github.com/storytold/figcraft.git
synced 2026-10-26 19:36:46 -06:00
Found and fixed in a Windows pass of everyday workflows: unsaved documents are now copied in the background (at most every 10 s) and offered back after a crash or forced quit (file.recover); Ctrl+arrow / Ctrl+Backspace / Ctrl+Delete text motion follows Windows and Linux conventions (keymap::text_motion); number-field scrubbing keeps its value; typing in a Tab-focused field works; default linear gradients and their handles behave; Escape closes dialogs without deselecting; clipped panel hints wrap; the unsaved-changes dialog is translated.
42 lines
1.7 KiB
Rust
42 lines
1.7 KiB
Rust
//! FigCraft's MCP server.
|
|
//!
|
|
//! [Model Context Protocol](https://modelcontextprotocol.io) over stdio: newline-delimited JSON-RPC
|
|
//! 2.0, hand-written (no async runtime). The server exposes FigCraft as MCP tools, resources and
|
|
//! prompts, and forwards everything to a [`Backend`]:
|
|
//!
|
|
//! - [`Headless`] hosts an in-process [`figcraft_engine::Session`] and answers the control-channel
|
|
//! methods itself (rendering with `figcraft-render`), so agents can design without a window.
|
|
//! - [`Attached`] talks to a running desktop app through its loopback JSON-lines control channel
|
|
//! (`figcraft --control PORT`): one `{"id","method","params"}` line in, one
|
|
//! `{"id","ok","result"|"error"}` line out.
|
|
//!
|
|
//! Entry points: [`Server::serve`] (stdio loop) and [`Server::handle_line`] (one message).
|
|
#![deny(clippy::unwrap_used, clippy::expect_used, clippy::panic, clippy::unimplemented, clippy::todo, clippy::unreachable)]
|
|
|
|
mod backend;
|
|
mod headless;
|
|
mod prompts;
|
|
mod resources;
|
|
mod server;
|
|
mod tools;
|
|
|
|
#[cfg(test)]
|
|
mod gradient_tests;
|
|
|
|
pub use backend::{Attached, Backend, decode_data, page_children};
|
|
pub use headless::Headless;
|
|
pub use prompts::{PROMPTS, PromptArg, PromptDef};
|
|
pub use resources::{COMMAND_TEMPLATE, COMMANDS_URI, DOC_URI, NODE_TEMPLATE};
|
|
pub use server::{INSTRUCTIONS, PROTOCOL_VERSION, SUPPORTED_VERSIONS, Server};
|
|
pub use tools::{TOOL_NAMES, ToolResult, call_tool, tool_definitions};
|
|
|
|
/// Width and height of a PNG from its IHDR chunk.
|
|
pub fn png_size(bytes: &[u8]) -> Option<(u32, u32)> {
|
|
if bytes.get(..8)? != b"\x89PNG\r\n\x1a\n" || bytes.get(12..16)? != b"IHDR" {
|
|
return None;
|
|
}
|
|
let w = u32::from_be_bytes(bytes.get(16..20)?.try_into().ok()?);
|
|
let h = u32::from_be_bytes(bytes.get(20..24)?.try_into().ok()?);
|
|
Some((w, h))
|
|
}
|