Implement stable serialization and restoration for dock layouts, including auto-hidden and floating panels

- Add `persistence.rs` for stable serialization of dock layouts without runtime identifiers.
- Introduce `preview.rs` for geometry handling of dock drop previews.
- Create `sizing.rs` to manage content-aware sizing policies and constraint solving for dock panels.
- Implement `welcome.rs` to provide a welcome surface when no documents are open, featuring New and Open actions.
This commit is contained in:
2026-07-16 22:10:22 +03:00
parent 1240d25011
commit b09795ccff
72 changed files with 11898 additions and 3115 deletions
+552 -102
View File
@@ -1,9 +1,18 @@
#![allow(dead_code, unused_imports, unused_variables, unused_macros, unreachable_patterns)]
use std::sync::{OnceLock, atomic::{AtomicU8, Ordering}};
use std::path::{Path, PathBuf};
use std::os::raw::c_char;
use std::ffi::CString;
#![allow(
dead_code,
unused_imports,
unused_variables,
unused_macros,
unreachable_patterns
)]
use libloading::Library;
use std::ffi::CString;
use std::os::raw::c_char;
use std::path::{Path, PathBuf};
use std::sync::{
atomic::{AtomicU8, Ordering},
OnceLock,
};
/// Global vision backend preference mirrored into the dynamic `hcie-vision` plugin.
/// 0 = CPU, 1 = GPU. Stored here so it can be set before the plugin is loaded.
@@ -48,15 +57,25 @@ pub fn map_brush_style(style: hcie_protocol::BrushStyle) -> hcie_brush_engine::B
}
}
pub fn blend_pixels(dst: [u8; 4], src: [u8; 4], mode: hcie_protocol::BlendMode, opacity: f32) -> [u8; 4] {
pub fn blend_pixels(
dst: [u8; 4],
src: [u8; 4],
mode: hcie_protocol::BlendMode,
opacity: f32,
) -> [u8; 4] {
hcie_blend::blend_pixels(dst, src, mode.into(), opacity)
}
pub fn blend_buffers(dst: &mut [u8], src: &[u8], mode: hcie_protocol::BlendMode, opacity: f32) {
hcie_blend::blend_buffers(dst, src, mode.into(), opacity)
}
pub fn apply_filter(filter_id: &str, params: &serde_json::Value, pixels: &mut [u8], width: u32, height: u32) -> Result<(), String> {
pub fn apply_filter(
filter_id: &str,
params: &serde_json::Value,
pixels: &mut [u8],
width: u32,
height: u32,
) -> Result<(), String> {
let mut fl = hcie_protocol::Layer::new_transparent("", width, height);
fl.pixels.copy_from_slice(pixels);
hcie_filter::apply_filter(&mut fl, filter_id, params);
@@ -65,69 +84,255 @@ pub fn apply_filter(filter_id: &str, params: &serde_json::Value, pixels: &mut [u
Ok(())
}
pub fn draw_line(layer: &mut hcie_protocol::Layer, x0: f32, y0: f32, x1: f32, y1: f32, color: [u8; 4], stroke_size: f32, mask: Option<&[u8]>) {
hcie_draw::draw_line(layer, x0, y0, x1, y1, color, stroke_size, mask); layer.dirty = true;
pub fn draw_line(
layer: &mut hcie_protocol::Layer,
x0: f32,
y0: f32,
x1: f32,
y1: f32,
color: [u8; 4],
stroke_size: f32,
mask: Option<&[u8]>,
) {
hcie_draw::draw_line(layer, x0, y0, x1, y1, color, stroke_size, mask);
layer.dirty = true;
}
pub fn draw_filled_rect(layer: &mut hcie_protocol::Layer, x1: f32, y1: f32, x2: f32, y2: f32, color: [u8; 4], mask: Option<&[u8]>) {
hcie_draw::draw_filled_rect(layer, x1, y1, x2, y2, color, mask); layer.dirty = true;
pub fn draw_filled_rect(
layer: &mut hcie_protocol::Layer,
x1: f32,
y1: f32,
x2: f32,
y2: f32,
color: [u8; 4],
mask: Option<&[u8]>,
) {
hcie_draw::draw_filled_rect(layer, x1, y1, x2, y2, color, mask);
layer.dirty = true;
}
pub fn draw_filled_ellipse(layer: &mut hcie_protocol::Layer, cx: f32, cy: f32, rx: f32, ry: f32, color: [u8; 4], mask: Option<&[u8]>) {
hcie_draw::draw_filled_ellipse(layer, cx, cy, rx, ry, color, mask); layer.dirty = true;
pub fn draw_filled_ellipse(
layer: &mut hcie_protocol::Layer,
cx: f32,
cy: f32,
rx: f32,
ry: f32,
color: [u8; 4],
mask: Option<&[u8]>,
) {
hcie_draw::draw_filled_ellipse(layer, cx, cy, rx, ry, color, mask);
layer.dirty = true;
}
pub fn flood_fill(layer: &mut hcie_protocol::Layer, x: u32, y: u32, color: [u8; 4], tolerance: u8, mask: Option<&[u8]>) {
hcie_draw::flood_fill(layer, x, y, color, tolerance, mask); layer.dirty = true;
pub fn flood_fill(
layer: &mut hcie_protocol::Layer,
x: u32,
y: u32,
color: [u8; 4],
tolerance: u8,
mask: Option<&[u8]>,
) {
hcie_draw::flood_fill(layer, x, y, color, tolerance, mask);
layer.dirty = true;
}
pub fn draw_brush_stroke(layer: &mut hcie_protocol::Layer, points: &[(f32, f32, f32)], color: [u8; 4], tip: &hcie_protocol::BrushTip, is_eraser: bool, mask: Option<&[u8]>, stroke_mask: Option<&mut [u8]>, bg_pixels: Option<&[u8]>) {
pub fn draw_brush_stroke(
layer: &mut hcie_protocol::Layer,
points: &[(f32, f32, f32)],
color: [u8; 4],
tip: &hcie_protocol::BrushTip,
is_eraser: bool,
mask: Option<&[u8]>,
stroke_mask: Option<&mut [u8]>,
bg_pixels: Option<&[u8]>,
) {
let style = map_brush_style(tip.style);
let ct = hcie_brush_engine::BrushTip {
style,
size: tip.size, opacity: tip.opacity, hardness: tip.hardness,
spacing: tip.spacing, flow: tip.flow,
jitter_amount: tip.jitter_amount, scatter_amount: tip.scatter_amount,
angle: tip.angle, roundness: tip.roundness,
spray_particle_size: tip.spray_particle_size, spray_density: tip.spray_density,
bitmap_pixels: tip.bitmap_pixels.clone(), bitmap_w: tip.bitmap_w, bitmap_h: tip.bitmap_h,
color_variant: tip.color_variant, variant_amount: tip.variant_amount, density: tip.density,
size: tip.size,
opacity: tip.opacity,
hardness: tip.hardness,
spacing: tip.spacing,
flow: tip.flow,
jitter_amount: tip.jitter_amount,
scatter_amount: tip.scatter_amount,
angle: tip.angle,
roundness: tip.roundness,
spray_particle_size: tip.spray_particle_size,
spray_density: tip.spray_density,
bitmap_pixels: tip.bitmap_pixels.clone(),
bitmap_w: tip.bitmap_w,
bitmap_h: tip.bitmap_h,
color_variant: tip.color_variant,
variant_amount: tip.variant_amount,
density: tip.density,
drawing_angle: false,
rotation_random: 0.0,
};
hcie_draw::draw_brush_stroke(layer, points, color, &ct, is_eraser, mask, stroke_mask, bg_pixels); layer.dirty = true;
hcie_draw::draw_brush_stroke(
layer,
points,
color,
&ct,
is_eraser,
mask,
stroke_mask,
bg_pixels,
);
layer.dirty = true;
}
pub fn draw_specialized_stroke(pixels: &mut [u8], width: u32, height: u32, points: &[(f32, f32, f32)], brush_style: hcie_protocol::BrushStyle, size: f32, hardness: f32, color: [u8; 4], opacity: f32, spacing_ratio: f32, is_eraser: bool, sketch_history: Option<&mut Vec<(f32, f32)>>, spray_particle_size: f32, spray_density: u32, mask: Option<&[u8]>, stroke_mask: Option<&mut [u8]>, bg_pixels: Option<&[u8]>, color_variant: bool, variant_amount: f32, density: f32) {
pub fn draw_specialized_stroke(
pixels: &mut [u8],
width: u32,
height: u32,
points: &[(f32, f32, f32)],
brush_style: hcie_protocol::BrushStyle,
size: f32,
hardness: f32,
color: [u8; 4],
opacity: f32,
spacing_ratio: f32,
is_eraser: bool,
sketch_history: Option<&mut Vec<(f32, f32)>>,
spray_particle_size: f32,
spray_density: u32,
mask: Option<&[u8]>,
stroke_mask: Option<&mut [u8]>,
bg_pixels: Option<&[u8]>,
color_variant: bool,
variant_amount: f32,
density: f32,
) {
let style = map_brush_style(brush_style);
hcie_brush_engine::draw_specialized_stroke(pixels, width, height, points, style, size, hardness, color, opacity, spacing_ratio, is_eraser, sketch_history, spray_particle_size, spray_density, mask, stroke_mask, bg_pixels, color_variant, variant_amount, density)
hcie_brush_engine::draw_specialized_stroke(
pixels,
width,
height,
points,
style,
size,
hardness,
color,
opacity,
spacing_ratio,
is_eraser,
sketch_history,
spray_particle_size,
spray_density,
mask,
stroke_mask,
bg_pixels,
color_variant,
variant_amount,
density,
)
}
pub fn composite_layers(layers: &[hcie_protocol::Layer], cw: u32, ch: u32) -> Vec<u8> { hcie_composite::composite_layers(layers, cw, ch) }
pub fn composite_layers_region(layers: &[hcie_protocol::Layer], cw: u32, ch: u32, x0: u32, y0: u32, x1: u32, y1: u32, out: &mut [u8]) { hcie_composite::composite_layers_region(layers, cw, ch, x0, y0, x1, y1, out) }
pub fn composite_layers(layers: &[hcie_protocol::Layer], cw: u32, ch: u32) -> Vec<u8> {
hcie_composite::composite_layers(layers, cw, ch)
}
pub fn composite_layers_region(
layers: &[hcie_protocol::Layer],
cw: u32,
ch: u32,
x0: u32,
y0: u32,
x1: u32,
y1: u32,
out: &mut [u8],
) {
hcie_composite::composite_layers_region(layers, cw, ch, x0, y0, x1, y1, out)
}
pub mod tiled {
pub fn composite_tiled_into(layers: &[hcie_protocol::Layer], tile_layers: &[Option<hcie_tile::TiledLayer>], cw: u32, ch: u32, x0: u32, y0: u32, x1: u32, y1: u32, out: &mut [u8]) {
hcie_composite::tiled::composite_tiled_into(layers, tile_layers, cw, ch, x0, y0, x1, y1, out)
pub fn composite_tiled_into(
layers: &[hcie_protocol::Layer],
tile_layers: &[Option<hcie_tile::TiledLayer>],
cw: u32,
ch: u32,
x0: u32,
y0: u32,
x1: u32,
y1: u32,
out: &mut [u8],
) {
hcie_composite::tiled::composite_tiled_into(
layers,
tile_layers,
cw,
ch,
x0,
y0,
x1,
y1,
out,
)
}
}
pub fn load_image(path: &Path) -> Result<hcie_protocol::Layer, String> { hcie_io::load_image(path) }
pub fn save_image(layer: &hcie_protocol::Layer, path: &Path, format: &str) -> Result<(), String> { hcie_io::save_image(layer, path, format) }
pub fn import_psd(path: &Path) -> Result<Vec<hcie_protocol::Layer>, String> { hcie_psd::import_psd(path) }
pub fn export_psd(layers: &[hcie_protocol::Layer], cw: u32, ch: u32, composited: &[u8], path: &Path) -> Result<(), String> { hcie_psd::save_psd(layers, cw, ch, composited, path) }
pub fn import_kra(path: &Path) -> Result<Vec<hcie_protocol::Layer>, String> { hcie_kra::import_kra(path) }
pub fn export_kra(layers: &[hcie_protocol::Layer], cw: u32, ch: u32, composited: &[u8], path: &Path) -> Result<(), String> { hcie_kra::export_kra(layers, cw, ch, composited, path) }
pub fn load_native(path: &Path) -> Result<Vec<hcie_protocol::Layer>, String> { hcie_native::load_native(path) }
pub fn save_native(layers: &[hcie_protocol::Layer], path: &Path) -> Result<(), String> { hcie_native::save_native(layers, path) }
pub fn load_image(path: &Path) -> Result<hcie_protocol::Layer, String> {
hcie_io::load_image(path)
}
pub fn save_image(layer: &hcie_protocol::Layer, path: &Path, format: &str) -> Result<(), String> {
hcie_io::save_image(layer, path, format)
}
pub fn import_psd(path: &Path) -> Result<Vec<hcie_protocol::Layer>, String> {
hcie_psd::import_psd(path)
}
pub fn export_psd(
layers: &[hcie_protocol::Layer],
cw: u32,
ch: u32,
composited: &[u8],
path: &Path,
) -> Result<(), String> {
hcie_psd::save_psd(layers, cw, ch, composited, path)
}
pub fn import_kra(path: &Path) -> Result<Vec<hcie_protocol::Layer>, String> {
hcie_kra::import_kra(path)
}
pub fn export_kra(
layers: &[hcie_protocol::Layer],
cw: u32,
ch: u32,
composited: &[u8],
path: &Path,
) -> Result<(), String> {
hcie_kra::export_kra(layers, cw, ch, composited, path)
}
pub fn load_native(path: &Path) -> Result<Vec<hcie_protocol::Layer>, String> {
hcie_native::load_native(path)
}
pub fn save_native(layers: &[hcie_protocol::Layer], path: &Path) -> Result<(), String> {
hcie_native::save_native(layers, path)
}
pub mod svg_import {
pub fn import_svg(data: &[u8]) -> Result<Vec<hcie_protocol::Layer>, String> { hcie_io::svg_import::import_svg(data) }
pub fn import_svg(data: &[u8]) -> Result<Vec<hcie_protocol::Layer>, String> {
hcie_io::svg_import::import_svg(data)
}
}
pub mod vector {
pub fn render_vector_shapes(layer: &mut hcie_protocol::Layer) { hcie_vector::render_vector_shapes(layer) }
pub fn render_vector_shapes(layer: &mut hcie_protocol::Layer) {
hcie_vector::render_vector_shapes(layer)
}
#[derive(Debug, Clone, Copy, PartialEq, serde::Serialize, serde::Deserialize)]
pub enum BooleanOp { Union, Intersect, Subtract, Xor }
pub fn boolean_shapes(op: BooleanOp, a: &hcie_protocol::VectorShape, b: &hcie_protocol::VectorShape) -> Option<hcie_protocol::VectorShape> {
let vop = match op { BooleanOp::Union => hcie_vector::path_boolean::BooleanOp::Union, BooleanOp::Intersect => hcie_vector::path_boolean::BooleanOp::Intersect, BooleanOp::Subtract => hcie_vector::path_boolean::BooleanOp::Subtract, BooleanOp::Xor => hcie_vector::path_boolean::BooleanOp::Xor };
pub enum BooleanOp {
Union,
Intersect,
Subtract,
Xor,
}
pub fn boolean_shapes(
op: BooleanOp,
a: &hcie_protocol::VectorShape,
b: &hcie_protocol::VectorShape,
) -> Option<hcie_protocol::VectorShape> {
let vop = match op {
BooleanOp::Union => hcie_vector::path_boolean::BooleanOp::Union,
BooleanOp::Intersect => hcie_vector::path_boolean::BooleanOp::Intersect,
BooleanOp::Subtract => hcie_vector::path_boolean::BooleanOp::Subtract,
BooleanOp::Xor => hcie_vector::path_boolean::BooleanOp::Xor,
};
hcie_vector::path_boolean::boolean_shapes(vop, a, b)
}
}
@@ -137,17 +342,29 @@ pub mod vector {
// ─────────────────────────────────────────────────────────────────────────────
fn find_plugin_path(name: &str) -> Result<PathBuf, String> {
let filename = if cfg!(target_os = "windows") { format!("{}.dll", name) } else if cfg!(target_os = "macos") { format!("lib{}.dylib", name) } else { format!("lib{}.so", name) };
let filename = if cfg!(target_os = "windows") {
format!("{}.dll", name)
} else if cfg!(target_os = "macos") {
format!("lib{}.dylib", name)
} else {
format!("lib{}.so", name)
};
let candidates: Vec<PathBuf> = {
let mut v = Vec::new();
// 1. Traditional plugins directory next to the executable or cwd.
v.push(PathBuf::from("plugins"));
if let Some(exe_plugins) = std::env::current_exe().ok().and_then(|p| p.parent().map(|d| d.join("plugins"))) {
if let Some(exe_plugins) = std::env::current_exe()
.ok()
.and_then(|p| p.parent().map(|d| d.join("plugins")))
{
v.push(exe_plugins.clone());
}
// 2. Cargo workspace layout: the dynamic library is placed directly in
// the target profile directory (e.g. target/debug/libhcie_ai.so).
if let Some(exe_dir) = std::env::current_exe().ok().and_then(|p| p.parent().map(|d| d.to_path_buf())) {
if let Some(exe_dir) = std::env::current_exe()
.ok()
.and_then(|p| p.parent().map(|d| d.to_path_buf()))
{
v.push(exe_dir.clone());
// 3. Also check the deps subdirectory used by Cargo.
v.push(exe_dir.join("deps"));
@@ -175,23 +392,35 @@ fn find_plugin_path(name: &str) -> Result<PathBuf, String> {
v.push(c.join("target").join("debug").join("deps"));
v.push(c.join("target").join("release"));
v.push(c.join("target").join("release").join("deps"));
if !c.pop() { break; }
if !c.pop() {
break;
}
}
}
v
};
for base in candidates {
let p = base.join(&filename);
if p.exists() { return Ok(p); }
if p.exists() {
return Ok(p);
}
}
Err(format!("Plugin '{}' not found", name))
}
macro_rules! sym { ($lib:ident, $name:literal) => { *$lib.get(concat!($name, "\0").as_bytes()).expect(concat!("Failed to resolve ", $name)) }; }
macro_rules! sym {
($lib:ident, $name:literal) => {
*$lib
.get(concat!($name, "\0").as_bytes())
.expect(concat!("Failed to resolve ", $name))
};
}
struct AiPlugins {
_lib_ai: Library, _lib_vision: Library,
ai_chat_complete_fn: unsafe extern "C" fn(*const c_char, *const c_char, *mut *mut u8, *mut usize) -> bool,
_lib_ai: Library,
_lib_vision: Library,
ai_chat_complete_fn:
unsafe extern "C" fn(*const c_char, *const c_char, *mut *mut u8, *mut usize) -> bool,
ai_session_create_fn: unsafe extern "C" fn(*const c_char) -> i64,
ai_session_push_user_fn: unsafe extern "C" fn(i64, *const c_char) -> bool,
ai_session_push_assistant_fn: unsafe extern "C" fn(i64, *const c_char) -> bool,
@@ -201,15 +430,43 @@ struct AiPlugins {
ai_template_names_fn: unsafe extern "C" fn(*mut *mut u8, *mut usize) -> bool,
ai_template_get_fn: unsafe extern "C" fn(*const c_char, *mut *mut u8, *mut usize) -> bool,
ai_template_search_fn: unsafe extern "C" fn(*const c_char, *mut *mut u8, *mut usize) -> bool,
ai_template_execute_fn: unsafe extern "C" fn(*const c_char, *const c_char, *mut *mut u8, *mut usize) -> bool,
ai_template_execute_fn:
unsafe extern "C" fn(*const c_char, *const c_char, *mut *mut u8, *mut usize) -> bool,
ai_execute_action_fn: unsafe extern "C" fn(*const c_char, *mut *mut u8, *mut usize) -> bool,
ai_free_buffer_fn: unsafe extern "C" fn(*mut u8, usize),
vision_background_removal_fn: unsafe extern "C" fn(*const u8, usize, u32, u32, *mut *mut u8, *mut usize) -> bool,
vision_object_segment_fn: unsafe extern "C" fn(*const u8, usize, u32, u32, u32, u32, u32, u32, u32, u32, *const c_char, *mut *mut u8, *mut usize) -> bool,
vision_super_resolution_fn: unsafe extern "C" fn(*const u8, usize, u32, u32, *mut *mut u8, *mut usize) -> bool,
vision_inpaint_fn: unsafe extern "C" fn(*mut u8, usize, u32, u32, *const u8, usize, i32) -> bool,
vision_build_polygon_mask_fn: unsafe extern "C" fn(*const c_char, u32, u32, *mut *mut u8, *mut usize) -> bool,
vision_build_stroke_mask_fn: unsafe extern "C" fn(*const c_char, *const u8, usize, f32, u32, u32, *mut *mut u8, *mut usize) -> bool,
vision_background_removal_fn:
unsafe extern "C" fn(*const u8, usize, u32, u32, *mut *mut u8, *mut usize) -> bool,
vision_object_segment_fn: unsafe extern "C" fn(
*const u8,
usize,
u32,
u32,
u32,
u32,
u32,
u32,
u32,
u32,
*const c_char,
*mut *mut u8,
*mut usize,
) -> bool,
vision_super_resolution_fn:
unsafe extern "C" fn(*const u8, usize, u32, u32, *mut *mut u8, *mut usize) -> bool,
vision_inpaint_fn:
unsafe extern "C" fn(*mut u8, usize, u32, u32, *const u8, usize, i32) -> bool,
vision_build_polygon_mask_fn:
unsafe extern "C" fn(*const c_char, u32, u32, *mut *mut u8, *mut usize) -> bool,
vision_build_stroke_mask_fn: unsafe extern "C" fn(
*const c_char,
*const u8,
usize,
f32,
u32,
u32,
*mut *mut u8,
*mut usize,
) -> bool,
vision_set_backend_fn: unsafe extern "C" fn(u8),
vision_free_buffer_fn: unsafe extern "C" fn(*mut u8, usize),
}
@@ -226,7 +483,8 @@ fn ai_plugins() -> &'static AiPlugins {
let ai_chat_complete_fn = *la.get(b"hcie_ai_chat_complete_c\0").unwrap();
let ai_session_create_fn = *la.get(b"hcie_ai_session_create_c\0").unwrap();
let ai_session_push_user_fn = *la.get(b"hcie_ai_session_push_user_c\0").unwrap();
let ai_session_push_assistant_fn = *la.get(b"hcie_ai_session_push_assistant_c\0").unwrap();
let ai_session_push_assistant_fn =
*la.get(b"hcie_ai_session_push_assistant_c\0").unwrap();
let ai_session_clear_fn = *la.get(b"hcie_ai_session_clear_c\0").unwrap();
let ai_session_destroy_fn = *la.get(b"hcie_ai_session_destroy_c\0").unwrap();
let ai_session_serialize_fn = *la.get(b"hcie_ai_session_serialize_c\0").unwrap();
@@ -236,26 +494,46 @@ fn ai_plugins() -> &'static AiPlugins {
let ai_template_execute_fn = *la.get(b"hcie_ai_template_execute_c\0").unwrap();
let ai_execute_action_fn = *la.get(b"hcie_ai_execute_action_c\0").unwrap();
let ai_free_buffer_fn = *la.get(b"free_buffer_c\0").unwrap();
let vision_background_removal_fn = *lv.get(b"hcie_vision_background_removal_c\0").unwrap();
let vision_background_removal_fn =
*lv.get(b"hcie_vision_background_removal_c\0").unwrap();
let vision_object_segment_fn = *lv.get(b"hcie_vision_object_segment_c\0").unwrap();
let vision_super_resolution_fn = *lv.get(b"hcie_vision_super_resolution_c\0").unwrap();
let vision_inpaint_fn = *lv.get(b"hcie_vision_inpaint_c\0").unwrap();
let vision_build_polygon_mask_fn = *lv.get(b"hcie_vision_build_polygon_mask_c\0").unwrap();
let vision_build_stroke_mask_fn = *lv.get(b"hcie_vision_build_stroke_mask_c\0").unwrap();
let vision_set_backend_fn: unsafe extern "C" fn(u8) = *lv.get(b"hcie_vision_set_backend_c\0").unwrap();
let vision_build_polygon_mask_fn =
*lv.get(b"hcie_vision_build_polygon_mask_c\0").unwrap();
let vision_build_stroke_mask_fn =
*lv.get(b"hcie_vision_build_stroke_mask_c\0").unwrap();
let vision_set_backend_fn: unsafe extern "C" fn(u8) =
*lv.get(b"hcie_vision_set_backend_c\0").unwrap();
let vision_free_buffer_fn = *lv.get(b"free_buffer_c\0").unwrap();
// Mirror the current backend preference into the freshly loaded plugin.
let backend = VISION_BACKEND.load(Ordering::Relaxed);
(vision_set_backend_fn)(backend);
AiPlugins {
_lib_ai: la, _lib_vision: lv,
ai_chat_complete_fn, ai_session_create_fn, ai_session_push_user_fn, ai_session_push_assistant_fn,
ai_session_clear_fn, ai_session_destroy_fn, ai_session_serialize_fn, ai_template_names_fn,
ai_template_get_fn, ai_template_search_fn, ai_template_execute_fn, ai_execute_action_fn, ai_free_buffer_fn,
vision_background_removal_fn, vision_object_segment_fn, vision_super_resolution_fn,
vision_inpaint_fn, vision_build_polygon_mask_fn, vision_build_stroke_mask_fn,
vision_set_backend_fn, vision_free_buffer_fn,
_lib_ai: la,
_lib_vision: lv,
ai_chat_complete_fn,
ai_session_create_fn,
ai_session_push_user_fn,
ai_session_push_assistant_fn,
ai_session_clear_fn,
ai_session_destroy_fn,
ai_session_serialize_fn,
ai_template_names_fn,
ai_template_get_fn,
ai_template_search_fn,
ai_template_execute_fn,
ai_execute_action_fn,
ai_free_buffer_fn,
vision_background_removal_fn,
vision_object_segment_fn,
vision_super_resolution_fn,
vision_inpaint_fn,
vision_build_polygon_mask_fn,
vision_build_stroke_mask_fn,
vision_set_backend_fn,
vision_free_buffer_fn,
}
}
})
@@ -275,26 +553,73 @@ pub fn vision_set_backend(backend: u8) {
pub mod ai {
use super::*;
macro_rules! c { ($e:expr) => { CString::new($e).unwrap() }; }
macro_rules! c {
($e:expr) => {
CString::new($e).unwrap()
};
}
pub fn chat_complete(config: &str, messages: &str) -> Result<Vec<u8>, String> {
let (mut op, mut ol) = (std::ptr::null_mut(), 0);
if ai!(ai_chat_complete_fn, c!(config).as_ptr(), c!(messages).as_ptr(), &mut op, &mut ol) {
let data = unsafe { std::slice::from_raw_parts(op, ol).to_vec() }; ai!(ai_free_buffer_fn, op, ol); Ok(data)
} else { Err("AI chat failed".into()) }
if ai!(
ai_chat_complete_fn,
c!(config).as_ptr(),
c!(messages).as_ptr(),
&mut op,
&mut ol
) {
let data = unsafe { std::slice::from_raw_parts(op, ol).to_vec() };
ai!(ai_free_buffer_fn, op, ol);
Ok(data)
} else {
Err("AI chat failed".into())
}
}
pub fn session_create(s: &str) -> i64 {
ai!(ai_session_create_fn, c!(s).as_ptr())
}
pub fn session_push_user(sid: i64, t: &str) -> bool {
ai!(ai_session_push_user_fn, sid, c!(t).as_ptr())
}
pub fn session_push_assistant(sid: i64, t: &str) -> bool {
ai!(ai_session_push_assistant_fn, sid, c!(t).as_ptr())
}
pub fn session_clear(sid: i64) -> bool {
ai!(ai_session_clear_fn, sid)
}
pub fn session_destroy(sid: i64) {
ai!(ai_session_destroy_fn, sid)
}
pub fn session_create(s: &str) -> i64 { ai!(ai_session_create_fn, c!(s).as_ptr()) }
pub fn session_push_user(sid: i64, t: &str) -> bool { ai!(ai_session_push_user_fn, sid, c!(t).as_ptr()) }
pub fn session_push_assistant(sid: i64, t: &str) -> bool { ai!(ai_session_push_assistant_fn, sid, c!(t).as_ptr()) }
pub fn session_clear(sid: i64) -> bool { ai!(ai_session_clear_fn, sid) }
pub fn session_destroy(sid: i64) { ai!(ai_session_destroy_fn, sid) }
pub fn session_serialize(sid: i64) -> Option<Vec<u8>> {
let (mut op, mut ol) = (std::ptr::null_mut(), 0);
if ai!(ai_session_serialize_fn, sid, &mut op, &mut ol) { let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() }; ai!(ai_free_buffer_fn, op, ol); Some(d) } else { None }
if ai!(ai_session_serialize_fn, sid, &mut op, &mut ol) {
let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() };
ai!(ai_free_buffer_fn, op, ol);
Some(d)
} else {
None
}
}
pub fn template_names() -> Option<Vec<u8>> {
let (mut op, mut ol) = (std::ptr::null_mut(), 0);
if ai!(ai_template_names_fn, &mut op, &mut ol) {
let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() };
ai!(ai_free_buffer_fn, op, ol);
Some(d)
} else {
None
}
}
pub fn template_get(name: &str) -> Option<Vec<u8>> {
let (mut op, mut ol) = (std::ptr::null_mut(), 0);
let n = CString::new(name).ok()?;
if ai!(ai_template_get_fn, n.as_ptr(), &mut op, &mut ol) {
let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() };
ai!(ai_free_buffer_fn, op, ol);
Some(d)
} else {
None
}
}
pub fn template_names() -> Option<Vec<u8>> { let (mut op, mut ol) = (std::ptr::null_mut(), 0);
if ai!(ai_template_names_fn, &mut op, &mut ol) { let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() }; ai!(ai_free_buffer_fn, op, ol); Some(d) } else { None } }
pub fn template_get(name: &str) -> Option<Vec<u8>> { let (mut op, mut ol) = (std::ptr::null_mut(), 0); let n = CString::new(name).ok()?;
if ai!(ai_template_get_fn, n.as_ptr(), &mut op, &mut ol) { let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() }; ai!(ai_free_buffer_fn, op, ol); Some(d) } else { None } }
/// # Purpose
/// Searches for AI prompt/action templates matching a specific query or tag.
///
@@ -322,39 +647,164 @@ pub mod ai {
None
}
}
pub fn template_execute(name: &str, params: &str) -> Option<Vec<u8>> { let (mut op, mut ol) = (std::ptr::null_mut(), 0);
pub fn template_execute(name: &str, params: &str) -> Option<Vec<u8>> {
let (mut op, mut ol) = (std::ptr::null_mut(), 0);
let (n, j) = (CString::new(name).ok()?, CString::new(params).ok()?);
if ai!(ai_template_execute_fn, n.as_ptr(), j.as_ptr(), &mut op, &mut ol) { let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() }; ai!(ai_free_buffer_fn, op, ol); Some(d) } else { None } }
pub fn execute_action(json: &str) -> Option<Vec<u8>> { let (mut op, mut ol) = (std::ptr::null_mut(), 0); let j = CString::new(json).ok()?;
if ai!(ai_execute_action_fn, j.as_ptr(), &mut op, &mut ol) { let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() }; ai!(ai_free_buffer_fn, op, ol); Some(d) } else { None } }
if ai!(
ai_template_execute_fn,
n.as_ptr(),
j.as_ptr(),
&mut op,
&mut ol
) {
let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() };
ai!(ai_free_buffer_fn, op, ol);
Some(d)
} else {
None
}
}
pub fn execute_action(json: &str) -> Option<Vec<u8>> {
let (mut op, mut ol) = (std::ptr::null_mut(), 0);
let j = CString::new(json).ok()?;
if ai!(ai_execute_action_fn, j.as_ptr(), &mut op, &mut ol) {
let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() };
ai!(ai_free_buffer_fn, op, ol);
Some(d)
} else {
None
}
}
}
pub mod vision {
use super::*;
pub fn background_removal(img: &[u8], w: u32, h: u32) -> Option<Vec<u8>> {
let (mut op, mut ol) = (std::ptr::null_mut(), 0);
if ai!(vision_background_removal_fn, img.as_ptr(), img.len(), w, h, &mut op, &mut ol) { let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() }; ai!(vision_free_buffer_fn, op, ol); Some(d) } else { None }
if ai!(
vision_background_removal_fn,
img.as_ptr(),
img.len(),
w,
h,
&mut op,
&mut ol
) {
let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() };
ai!(vision_free_buffer_fn, op, ol);
Some(d)
} else {
None
}
}
pub fn object_segment(img: &[u8], w: u32, h: u32, px: u32, py: u32, bx0: u32, by0: u32, bx1: u32, by1: u32, model_path: &str) -> Option<Vec<u8>> {
pub fn object_segment(
img: &[u8],
w: u32,
h: u32,
px: u32,
py: u32,
bx0: u32,
by0: u32,
bx1: u32,
by1: u32,
model_path: &str,
) -> Option<Vec<u8>> {
let (mut op, mut ol) = (std::ptr::null_mut(), 0);
let mp = CString::new(model_path).unwrap_or_default();
if ai!(vision_object_segment_fn, img.as_ptr(), img.len(), w, h, px, py, bx0, by0, bx1, by1, mp.as_ptr(), &mut op, &mut ol) { let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() }; ai!(vision_free_buffer_fn, op, ol); Some(d) } else { None }
if ai!(
vision_object_segment_fn,
img.as_ptr(),
img.len(),
w,
h,
px,
py,
bx0,
by0,
bx1,
by1,
mp.as_ptr(),
&mut op,
&mut ol
) {
let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() };
ai!(vision_free_buffer_fn, op, ol);
Some(d)
} else {
None
}
}
pub fn super_resolution(img: &[u8], w: u32, h: u32) -> Option<Vec<u8>> {
let (mut op, mut ol) = (std::ptr::null_mut(), 0);
if ai!(vision_super_resolution_fn, img.as_ptr(), img.len(), w, h, &mut op, &mut ol) { let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() }; ai!(vision_free_buffer_fn, op, ol); Some(d) } else { None }
if ai!(
vision_super_resolution_fn,
img.as_ptr(),
img.len(),
w,
h,
&mut op,
&mut ol
) {
let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() };
ai!(vision_free_buffer_fn, op, ol);
Some(d)
} else {
None
}
}
pub fn inpaint(pixels: &mut [u8], w: u32, h: u32, mask: &[u8], r: i32) -> bool {
ai!(
vision_inpaint_fn,
pixels.as_mut_ptr(),
pixels.len(),
w,
h,
mask.as_ptr(),
mask.len(),
r
)
}
pub fn inpaint(pixels: &mut [u8], w: u32, h: u32, mask: &[u8], r: i32) -> bool { ai!(vision_inpaint_fn, pixels.as_mut_ptr(), pixels.len(), w, h, mask.as_ptr(), mask.len(), r) }
pub fn build_polygon_mask(json: &str, w: u32, h: u32) -> Option<Vec<u8>> {
let (mut op, mut ol) = (std::ptr::null_mut(), 0); let j = CString::new(json).ok()?;
if ai!(vision_build_polygon_mask_fn, j.as_ptr(), w, h, &mut op, &mut ol) { let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() }; ai!(vision_free_buffer_fn, op, ol); Some(d) } else { None }
let (mut op, mut ol) = (std::ptr::null_mut(), 0);
let j = CString::new(json).ok()?;
if ai!(
vision_build_polygon_mask_fn,
j.as_ptr(),
w,
h,
&mut op,
&mut ol
) {
let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() };
ai!(vision_free_buffer_fn, op, ol);
Some(d)
} else {
None
}
}
pub fn build_stroke_mask(json: &str, sel: &[u8], r: f32, w: u32, h: u32) -> Option<Vec<u8>> {
let (mut op, mut ol) = (std::ptr::null_mut(), 0); let j = CString::new(json).ok()?;
if ai!(vision_build_stroke_mask_fn, j.as_ptr(), sel.as_ptr(), sel.len(), r, w, h, &mut op, &mut ol) { let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() }; ai!(vision_free_buffer_fn, op, ol); Some(d) } else { None }
let (mut op, mut ol) = (std::ptr::null_mut(), 0);
let j = CString::new(json).ok()?;
if ai!(
vision_build_stroke_mask_fn,
j.as_ptr(),
sel.as_ptr(),
sel.len(),
r,
w,
h,
&mut op,
&mut ol
) {
let d = unsafe { std::slice::from_raw_parts(op, ol).to_vec() };
ai!(vision_free_buffer_fn, op, ol);
Some(d)
} else {
None
}
}
/// Forward the CPU/GPU backend preference to the dynamic `hcie-vision` plugin.
pub fn set_backend(backend: u8) {
crate::dynamic_loader::vision_set_backend(backend);
}
}
}