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 Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 11 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -218,3 +218,14 @@ clean-helpers:
rm -rf $(HELPER_STAGE)

FORCE:

# README's text layout and GPU effect work.
.PHONY: check-text check-gpu-fx check-readme
check-text:
$(CARGO) test -p schist-core -p schist-text-engine -p schist-tools-type -p schist-codec-affinity
check-gpu-fx:
$(CARGO) test -p schist-fx
$(CARGO) test -p schist-compositor-gpu --test fx_parity --test fx_wiring
check-readme: check-text check-gpu-fx
$(CARGO) clippy -p schist-core -p schist-text-engine -p schist-tools-type -p schist-codec-affinity -p schist-fx -p schist-compositor-gpu --all-targets -- -D warnings
$(CARGO) check -p schist-app
31 changes: 18 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,7 +137,10 @@ Path can fill, stroke or convert them to a selection. Rectangle, ellipse,
line (with its own weight, 45° constrain and arrowheads), polygon and six
custom shapes — as **live shape layers** by default, which keep their
path, regenerate their pixels from it, and survive a PSD round trip as
vectors. Editable text layers.
vectors. Editable text layers, including **text on paths** with a baseline
offset and alignment, plus **OpenType controls** for kerning, ligatures,
discretionary ligatures and small caps. Text settings survive PSD/PSB
save and reopen. See [docs/text.md](docs/text.md).

**Non-destructive.** Sixteen adjustments — levels, curves, hue/saturation,
brightness/contrast, black & white, colour balance, vibrance, exposure,
Expand Down Expand Up @@ -211,7 +214,12 @@ a lens blur at radius 60 is eleven thousand taps a pixel. **Content-Aware
Scale** runs there too, and runs *entirely* there: find the lowest-energy
seam, cut it, start again is hundreds of full-image passes for one
command, so the whole loop stays on the device and only the finished image
comes back. The CPU is the semantic reference throughout: parity tests
comes back. **Large warps and seam carves can exceed a storage-buffer
binding**: warp sources live in texture arrays and their output is banded;
large carves keep their image planes in texture arrays and cumulative costs
in two rows. Device texture limits and available memory still apply, and
small carves stay on the CPU when dispatch overhead would cost more than
the GPU saves. The CPU is the semantic reference throughout: parity tests
hold the GPU to it, anything it can't express (layers mid-drag) or can't
fit falls back for that call, and machines with no usable adapter just run
the CPU path. Toggle it in
Expand Down Expand Up @@ -280,17 +288,13 @@ Remap anything in `~/.config/schist/keymap.json`:

- **CMYK and Lab edit in RGB.** Files open, edit and save in their own
mode, converting at the boundaries; the editing in between is RGB, so
individual ink channels are not separately editable.
- **Text is not on a path**, and the type engine has no OpenType
feature controls.
- **The mesh warp and the carve need the layer to fit one storage
binding.** A displacement may read anywhere in its source, and every
seam depends on the one before it over the whole image, so neither can
be split into bands the way the blurs are; on adapters at the 128 MB
baseline a large layer falls back to the CPU. Real GPUs report bindings
in the gigabytes and never reach it. The carve also stays on the CPU
below a couple of megapixels, where its row-by-row scan costs more in
dispatches than it saves.
individual ink channels are not separately editable. This remains
unresolved because tiles, editing operations and plugin APIs carry
RGBA, and import has already converted the native channels to RGB.
Correct native editing needs a coordinated storage, undo, compositor
and plugin-contract migration; RGB-derived controls cannot recover
independent CMYK separations. See the [implementation constraints and
remaining work](docs/native-colour-editing.md).

## Diagnostics

Expand Down Expand Up @@ -368,6 +372,7 @@ already logged into. See [docs/ai-panel.md](docs/ai-panel.md).
* [docs/architecture.md](docs/architecture.md) — how the pieces fit
* [docs/gallery.md](docs/gallery.md) — the Picasa-style photo gallery
* [docs/plugin-guide.md](docs/plugin-guide.md) — writing plugins
* [docs/text.md](docs/text.md) — OpenType controls and text on paths
* [docs/mcp.md](docs/mcp.md) — the MCP server
* [docs/ai-panel.md](docs/ai-panel.md) — the in-app AI sidebar
* [docs/quicklook.md](docs/quicklook.md) — the macOS Quick Look extensions
Expand Down
1 change: 1 addition & 0 deletions crates/app/assets/icons/character.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions crates/app/assets/icons/type-align-center.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions crates/app/assets/icons/type-align-left.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions crates/app/assets/icons/type-align-right.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 4 additions & 0 deletions crates/app/src/assets.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ macro_rules! icons {
}

icons!(
"character",
"type-align-left",
"type-align-center",
"type-align-right",
"move",
"swap",
"eyedropper",
Expand Down
29 changes: 21 additions & 8 deletions crates/app/src/panels/info.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,26 @@ const INFO_ROWS_MAX_H: f32 = 88.0;
#[cfg(not(target_arch = "wasm32"))]
const SIDE_PANEL_CONTENT_W: f32 = 260.0 - 16.0;

/// The side panel's top slot: a tab row when the open file has EXIF
/// Info first and by default, Color beside it — and the plain colour
/// panel when it has none, exactly as before.
/// The top dock: Character while using Type, Info for files with EXIF,
/// and Color. Type's detailed controls stay here instead of wrapping the
/// options bar onto the canvas.
pub(super) fn top_panel(ws: &mut Workspace, cx: &mut Context<Workspace>) -> gpui::AnyElement {
ws.refresh_exif();
let Some(exif) = ws.exif.as_ref().and_then(|(_, e)| e.clone()) else {
let exif = ws.exif.as_ref().and_then(|(_, e)| e.clone());
let is_type = ws.editor.active_tool == "type";
if exif.is_none() && !is_type {
return color_panel(ws, cx).into_any_element();
}
let default = if is_type {
SideTab::Character
} else {
SideTab::Info
};
let tab = match ws.side_tab.unwrap_or(default) {
SideTab::Character if !is_type => default,
SideTab::Info if exif.is_none() => SideTab::Color,
tab => tab,
};
let tab = ws.side_tab.unwrap_or(SideTab::Info);
let tab_chip = |label: &'static str, which: SideTab, cx: &mut Context<Workspace>| {
let on = tab == which;
div()
Expand All @@ -47,19 +58,21 @@ pub(super) fn top_panel(ws: &mut Workspace, cx: &mut Context<Workspace>) -> gpui
cx.notify();
}),
)
.child(label.to_uppercase())
.child(label)
};
let tabs = div()
.flex()
.flex_row()
.gap_1()
.px_2()
.pt_2()
.child(tab_chip("Info", SideTab::Info, cx))
.children(is_type.then(|| tab_chip("Character", SideTab::Character, cx)))
.children(exif.is_some().then(|| tab_chip("Info", SideTab::Info, cx)))
.child(tab_chip("Color", SideTab::Color, cx));
let body = match tab {
SideTab::Info => info_panel(ws, &exif, cx).into_any_element(),
SideTab::Info => info_panel(ws, exif.as_ref().unwrap(), cx).into_any_element(),
SideTab::Color => color_panel(ws, cx).into_any_element(),
SideTab::Character => super::typography::character_panel(ws, cx),
};
div()
.flex()
Expand Down
1 change: 1 addition & 0 deletions crates/app/src/panels/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ mod status;
mod tabs;
mod titlebar;
mod toolbar;
mod typography;

#[cfg(not(target_arch = "wasm32"))]
pub use ai::*;
Expand Down
18 changes: 14 additions & 4 deletions crates/app/src/panels/toolbar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ pub(super) type ToolSlot = (

pub fn tool_options_bar(ws: &mut Workspace, cx: &mut Context<Workspace>) -> impl IntoElement {
let tool_id = ws.editor.active_tool;
if tool_id == "type" {
return super::typography::type_options_bar(ws, cx).into_any_element();
}
let (tool_icon, tool_name) = ws
.registry
.tools()
Expand All @@ -27,11 +30,16 @@ pub fn tool_options_bar(ws: &mut Workspace, cx: &mut Context<Workspace>) -> impl
let mut bar = div()
.flex()
.flex_row()
.flex_wrap()
.items_center()
.gap_4()
.h(px(32.0))
.gap_x_4()
.gap_y_1()
.min_h(px(32.0))
.w_full()
.min_w_0()
.flex_none()
.px_3()
.py_1()
.bg(gpui::rgb(palette().panel_bg))
.border_b_1()
.border_color(gpui::rgb(palette().panel_edge))
Expand Down Expand Up @@ -84,9 +92,11 @@ pub fn tool_options_bar(ws: &mut Workspace, cx: &mut Context<Workspace>) -> impl
.map(|t| t.options())
.unwrap_or_default()
{
bar = bar.child(tool_option_control(ws, opt, cx));
// Keep each control together when a tool has more options than
// one row can show. Type has its own compact bar and panel.
bar = bar.child(div().flex_none().child(tool_option_control(ws, opt, cx)));
}
bar
bar.into_any_element()
}

/// Render one plugin-declared option. The shell knows the three kinds, not
Expand Down
Loading
Loading