feat: add native clipboard support for screenshot paste #67

Merged
naomi merged 8 commits from feat/keep-workin into main 2026-01-25 13:08:38 -08:00
Showing only changes of commit a191bdef23 - Show all commits
+129
View File
@@ -1,5 +1,6 @@
<script lang="ts">
import { invoke } from "@tauri-apps/api/core";
import { open } from "@tauri-apps/plugin-dialog";
import { get } from "svelte/store";
import { claudeStore, isClaudeProcessing } from "$lib/stores/claude";
import { characterState } from "$lib/stores/character";
@@ -300,6 +301,86 @@ User: ${formattedMessage}`;
claudeStore.removeAttachment(id);
}
async function handleFilePicker() {
try {
const selected = await open({
multiple: true,
filters: [
{
name: "All Files",
extensions: ["*"],
},
{
name: "Images",
extensions: ["png", "jpg", "jpeg", "gif", "webp", "svg", "bmp"],
},
{
name: "Documents",
extensions: ["pdf", "txt", "md", "doc", "docx", "csv", "json", "xml"],
},
{
name: "Code",
extensions: [
"js",
"ts",
"jsx",
"tsx",
"py",
"rs",
"go",
"java",
"c",
"cpp",
"h",
"hpp",
"css",
"html",
"svelte",
"vue",
],
},
],
});
if (!selected) return;
// Handle both single and multiple file selection
const files = Array.isArray(selected) ? selected : [selected];
for (const filePath of files) {
// Get file info
const filename = filePath.split(/[/\\]/).pop() || "unknown";
const extension = filename.split(".").pop()?.toLowerCase() || "";
// Determine file type
const imageExtensions = ["png", "jpg", "jpeg", "gif", "webp", "svg", "bmp"];
const documentExtensions = ["pdf", "txt", "md", "doc", "docx", "csv", "json", "xml"];
let fileType: "image" | "document" | "other";
if (imageExtensions.includes(extension)) {
fileType = "image";
} else if (documentExtensions.includes(extension)) {
fileType = "document";
} else {
fileType = "other";
}
// Create attachment
const attachment: Attachment = {
id: `attachment-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`,
filename,
path: filePath,
size: 0, // We don't have easy access to file size from the dialog
type: fileType,
};
claudeStore.addAttachment(attachment);
}
} catch (error) {
console.error("Failed to open file picker:", error);
}
}
function handleKeyDown(event: KeyboardEvent) {
// Handle command menu navigation
if (showCommandMenu && matchingCommands.length > 0) {
@@ -397,6 +478,28 @@ User: ${formattedMessage}`;
</div>
<div class="button-wrapper">
<button
type="button"
onclick={handleFilePicker}
class="attach-button"
title="Attach files"
>
<svg
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
>
<path
d="M21.44 11.05l-9.19 9.19a6 6 0 0 1-8.49-8.49l9.19-9.19a4 4 0 0 1 5.66 5.66l-9.2 9.19a2 2 0 0 1-2.83-2.83l8.49-8.48"
/>
</svg>
</button>
{#if isProcessing}
<button
type="button"
@@ -482,6 +585,32 @@ User: ${formattedMessage}`;
height: 100%;
}
.attach-button {
display: flex;
align-items: center;
justify-content: center;
width: 48px;
height: 48px;
padding: 0;
background: var(--bg-secondary);
border: 1px solid var(--border-color);
border-radius: 8px;
color: var(--text-secondary);
cursor: pointer;
transition: all 0.2s;
}
.attach-button:hover {
background: var(--bg-tertiary);
border-color: var(--accent-primary);
color: var(--accent-primary);
transform: scale(1.05);
}
.attach-button:active {
transform: scale(0.95);
}
.send-button {
padding: 0 24px;
height: 48px;