项目文件夹

文件
wehub-resource-sync adf0d17497
publish / version_or_publish (push) Has been cancelled
storybook-build / changes (push) Has been cancelled
storybook-build / :storybook-build (push) Has been cancelled
Sync Gradio Skills to Hugging Face / sync-skills (push) Has been cancelled
functional / changes (push) Has been cancelled
functional / build-frontend (push) Has been cancelled
functional / functional-test-SSR=false (push) Has been cancelled
functional / functional-reload (push) Has been cancelled
js / changes (push) Has been cancelled
js / js-test (push) Has been cancelled
docs-build / changes (push) Has been cancelled
docs-build / docs-build (push) Has been cancelled
docs-build / website-build (push) Has been cancelled
functional / functional-test-SSR=true (push) Has been cancelled
hygiene / hygiene-test (push) Has been cancelled
python / changes (push) Has been cancelled
python / build (push) Has been cancelled
python / test-ubuntu-latest-flaky (push) Has been cancelled
python / test-ubuntu-latest-not-flaky (push) Has been cancelled
python / test-windows-latest-flaky (push) Has been cancelled
python / test-windows-latest-not-flaky (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:17:32 +08:00

368 行
8.0 KiB
Svelte

<script lang="ts" context="module">
export type Tool = "image" | "draw" | "erase" | "pan";
export type Subtool =
| "upload"
| "paste"
| "webcam"
| "color"
| "size"
| "crop"
| "remove_background"
| null;
</script>
<script lang="ts">
import IconButton from "./IconButton.svelte";
import {
Image,
Brush,
BrushSize,
Erase,
Crop,
Upload,
ImagePaste,
Webcam,
Resize,
ColorPickerSolid
} from "@gradio/icons";
import BrushOptions from "./brush/BrushOptions.svelte";
import type { Source, Transform } from "./types";
import {
type Brush as BrushType,
type Eraser as EraserType
} from "./brush/types";
let {
tool = "image",
subtool = null,
background = false,
brush_options,
selected_size = $bindable(25),
eraser_options,
selected_eraser_size = $bindable(25),
selected_color = $bindable(),
selected_opacity = $bindable(1),
preview = $bindable(),
show_brush_color = false,
show_brush_size = false,
show_eraser_size = false,
sources,
transforms,
ontool_change,
onsubtool_change
}: {
tool?: Tool;
subtool?: Subtool;
background?: boolean;
brush_options: BrushType | false;
selected_size?: number;
eraser_options: EraserType | false;
selected_eraser_size?: number;
selected_color?: any;
selected_opacity?: number;
preview?: boolean;
show_brush_color?: boolean;
show_brush_size?: boolean;
show_eraser_size?: boolean;
sources: Source[];
transforms: Transform[];
ontool_change?: (value: { tool: Tool }) => void;
onsubtool_change?: (value: { tool: Tool; subtool: Subtool }) => void;
} = $props();
let recent_colors = $state<string[]>([]);
/**
* Handles tool click events
* @param {Tool} _tool - The selected tool
*/
function handle_tool_click(e: Event, _tool: Tool): void {
e.stopPropagation();
ontool_change?.({ tool: _tool });
}
/**
* Handles subtool click events
* @param {Event} e - The click event
* @param {Subtool} _subtool - The selected subtool
*/
function handle_subtool_click(e: Event, _subtool: typeof subtool): void {
e.stopPropagation();
onsubtool_change?.({ tool, subtool: _subtool });
}
$effect(() => {
show_brush_size = tool === "draw" && subtool === "size";
show_brush_color = tool === "draw" && subtool === "color";
show_eraser_size = tool === "erase" && subtool === "size";
});
let can_crop = $derived(transforms.includes("crop"));
let can_resize = $derived(transforms.includes("resize"));
let can_upload = $derived(sources.includes("upload"));
let can_webcam = $derived(sources.includes("webcam"));
let can_paste = $derived(sources.includes("clipboard"));
let can_edit_image = $derived(
sources.length > 0 || (background && transforms.length > 0)
);
</script>
<div class="toolbar-wrap" onclick={(e) => e.stopPropagation()}>
<div class="half-container">
{#if can_edit_image}
<IconButton
Icon={Image}
label="Image"
highlight={tool === "image"}
onclick={(e) => handle_tool_click(e, "image")}
size="medium"
padded={false}
transparent={true}
/>
{/if}
{#if brush_options}
<IconButton
Icon={Brush}
label="Brush"
onclick={(e) => handle_tool_click(e, "draw")}
highlight={tool === "draw"}
size="medium"
padded={false}
transparent={true}
/>
{/if}
{#if eraser_options}
<IconButton
Icon={Erase}
label="Erase"
onclick={(e) => handle_tool_click(e, "erase")}
highlight={tool === "erase"}
size="medium"
padded={false}
transparent={true}
/>
{/if}
</div>
<div
class="half-container right"
class:hide={tool === "pan" ||
(tool === "image" && !background && sources.length === 0) ||
(tool === "image" && background && transforms.length === 0)}
>
{#if tool === "image"}
{#if background}
{#if can_crop}
<IconButton
Icon={Crop}
label="Crop"
onclick={(e) => handle_subtool_click(e, "crop")}
highlight={subtool === "crop"}
size="medium"
padded={false}
transparent={true}
offset={0}
/>
{/if}
{#if can_resize}
<IconButton
Icon={Resize}
label="Resize"
onclick={(e) => handle_subtool_click(e, "size")}
highlight={subtool === "size"}
size="medium"
padded={false}
transparent={true}
offset={0}
/>
{/if}
{:else}
{#if can_upload}
<IconButton
Icon={Upload}
label="Upload"
onclick={(e) => handle_subtool_click(e, "upload")}
highlight={subtool === "upload"}
size="medium"
padded={false}
transparent={true}
offset={0}
/>
{/if}
{#if can_paste}
<IconButton
Icon={ImagePaste}
label="Paste"
onclick={(e) => handle_subtool_click(e, "paste")}
highlight={subtool === "paste"}
size="large"
padded={false}
transparent={true}
offset={0}
/>
{/if}
{#if can_webcam}
<IconButton
Icon={Webcam}
label="Webcam"
onclick={(e) => handle_subtool_click(e, "webcam")}
highlight={subtool === "webcam"}
size="medium"
padded={false}
transparent={true}
offset={0}
/>
{/if}
{/if}
{/if}
{#if tool === "draw" && brush_options}
<IconButton
Icon={ColorPickerSolid}
label="Color"
color={selected_color}
onclick={(e) => handle_subtool_click(e, "color")}
size="medium"
padded={false}
transparent={true}
offset={0}
/>
<IconButton
Icon={BrushSize}
label="Brush Size"
onclick={(e) => handle_subtool_click(e, "size")}
highlight={subtool === "size"}
size="medium"
padded={false}
transparent={true}
offset={0}
/>
{#if show_brush_color || show_brush_size}
<BrushOptions
colors={brush_options.colors}
color_mode={brush_options.color_mode}
{recent_colors}
show_swatch={show_brush_color}
show_size={show_brush_size}
bind:selected_size
bind:selected_color
bind:selected_opacity
bind:preview
onclick_outside={(e) => {
e.stopPropagation();
preview = false;
show_brush_color = false;
show_brush_size = false;
handle_subtool_click(e, null);
}}
mode="brush"
/>
{/if}
{/if}
{#if tool === "erase" && eraser_options}
<IconButton
Icon={BrushSize}
label="Eraser Size"
onclick={(e) => handle_subtool_click(e, "size")}
highlight={subtool === "size"}
size="medium"
padded={false}
transparent={true}
offset={0}
/>
{/if}
{#if show_eraser_size}
<BrushOptions
colors={[]}
show_swatch={false}
show_size={true}
bind:selected_size={selected_eraser_size}
bind:selected_color
bind:selected_opacity
bind:preview
onclick_outside={(e) => {
e.stopPropagation();
preview = false;
show_eraser_size = false;
handle_subtool_click(e, null);
}}
mode="eraser"
/>
{/if}
</div>
</div>
<style>
.toolbar-wrap {
display: inline-flex;
flex-direction: column;
justify-content: center;
align-items: center;
position: absolute;
left: 15px;
right: 0;
top: 30%;
/* transform: translateY(-50%); */
width: 30px;
border: 1px solid var(--block-border-color);
border-radius: var(--radius-md);
gap: var(--spacing-sm);
z-index: 1000;
background-color: var(--block-background-fill);
}
.half-container {
flex: 1;
display: flex;
flex-direction: column;
gap: var(--spacing-sm);
position: relative;
}
.half-container {
width: 100%;
padding: var(--spacing-sm) 0;
}
.half-container:last-child {
border-top: 1px solid var(--block-border-color);
}
.hide {
display: none;
}
/* .toolbar-left,
.toolbar-right {
display: flex;
flex-direction: row;
align-items: center;
gap: var(--spacing-sm);
display: flex;
z-index: 1000;
border-radius: var(--radius-sm);
background-color: #fff;
padding: var(--spacing-sm) 0.3rem;
width: auto;
}
.toolbar-right {
border-left: 0;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
}
.toolbar-left {
border-top-right-radius: 0;
border-bottom-right-radius: 0;
}
.toolbar-layers {
border-top-left-radius: 0;
border-bottom-left-radius: 0;
} */
</style>