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
32 changes: 32 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,38 @@ popover, not for the person who wrote the commit.

## [Unreleased]

## [1.8.10] - 2026-09-27

### Changed
- **Deleting a context now deletes the stashes in it, and the confirmation says how many.**
They used to stay behind, filed under a context that no longer existed: no view showed
them, yet they were still stored, counted and synced. Deleted stashes stay recoverable on
the cloud for about 30 days. The Default context still cannot be deleted

### Fixed
- **Importing a `.zip` export now actually finds its attachments.** The importer looked for
each file under the *new* id an imported stash gets, which is never the id the archive was
written with, so no attachment from an export ever survived an import. Older exports still
import correctly
- **Exporting a context with two attachments of the same name no longer fails silently.**
Two pasted screenshots, both called `image.png`, made the export archive stop after the
first one - and the dialog gave no sign anything had gone wrong. Files are now named by
attachment id inside the archive, so a repeated name no longer collides
- **An export refuses to leave a file out rather than write an incomplete archive.** An
attachment synced from another device but not yet downloaded used to be dropped from the
archive while the document still linked to it. The export now fetches anything missing
and tries again, or says exactly which file it could not include
- **A failed export or import now shows why, instead of leaving the dialog open with nothing
said.** Both used to fail silently to the console
- **Stashes left behind by an earlier context deletion are cleaned up.** After this update,
any stash still filed under a deleted context - on this device or in the cloud - is deleted
like its context was. You could not see these stashes anyway
- **Every stash now belongs to a context, the same way here and in the cloud.** A stash
saved by an older version, imported from an older export, or created by an AI agent could
end up with no context at all. The queue showed it under Default, but clearing completed
stashes there, exporting, and the cloud all missed it. Such a stash is now filed under
Default everywhere

## [1.8.9] - 2026-09-26

### Security
Expand Down
2 changes: 1 addition & 1 deletion TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ mod tests {
attachments: vec![],
files: vec![],
created_at: chrono::Utc::now().to_rfc3339(),
context_id: Some("default".to_string()),
context_id: "default".to_string(),
completed: false,
completed_at: None,
};
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "stashpad",
"version": "1.8.9",
"version": "1.8.10",
"description": "The staging area for your AI context.",
"author": {
"name": "Nico Wiedemann",
Expand Down
2 changes: 1 addition & 1 deletion src-tauri/Cargo.lock

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

2 changes: 1 addition & 1 deletion src-tauri/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "stashpad"
version = "1.8.9"
version = "1.8.10"
description = "The staging area for your AI context."
authors = ["Nico Wiedemann <nico.wiedemann@gmail.com>"]
repository = "https://github.com/EarMaster/stashpad"
Expand Down
14 changes: 8 additions & 6 deletions src-tauri/src/account_export.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,13 @@ async fn parse_export(

stashes.push(StashItem {
id,
context_id: raw["context_id"].as_str().map(str::to_string),
// An export from before every stash had a context carries null here, which
// lands in the default context - where the app showed it all along.
context_id: raw["context_id"]
.as_str()
.filter(|id| !id.is_empty())
.unwrap_or(crate::models::DEFAULT_CONTEXT_ID)
.to_string(),
content,
files: Vec::new(),
created_at: raw["created_at"].as_str().unwrap_or_default().to_string(),
Expand Down Expand Up @@ -303,11 +309,7 @@ pub async fn export_whole_account(

let mut by_context: HashMap<String, Vec<String>> = HashMap::new();
for stash in stashes {
let owner = stash
.context_id
.clone()
.unwrap_or_else(|| "default".to_string());
by_context.entry(owner).or_default().push(stash.id);
by_context.entry(stash.context_id).or_default().push(stash.id);
}
(contexts, by_context)
};
Expand Down
18 changes: 11 additions & 7 deletions src-tauri/src/contexts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,18 +86,22 @@ pub async fn save_context(state: State<'_, Arc<DbState>>, context: Context) -> R
/// edited and push it straight back on the next sync.
#[tauri::command]
pub async fn import_contexts(state: State<'_, Arc<DbState>>, contexts: Vec<Context>) -> Result<(), UiError> {
state
.lock_db()
.import_contexts(&contexts)
.map_err(|e| e.to_string())
.map_err(UiError::from)
let mut db = state.lock_db();
// A context deleted on another device takes its stashes with it here too.
let deleted = db.import_contexts(&contexts).map_err(|e| e.to_string())?;
crate::stashes::purge_deleted_stash_files(&db, &deleted);
Ok(())
}

/// Delete a context and every stash filed under it - the same rule the cloud applies, so
/// the result is the same whichever side the deletion starts on.
#[tauri::command]
pub async fn delete_context(state: State<'_, Arc<DbState>>, id: String) -> Result<(), UiError> {
println!("Deleting context: {}", id);
if let Err(e) = state.lock_db().delete_context(&id) {
println!("Failed to delete context: {}", e);
let mut db = state.lock_db();
match db.delete_context(&id) {
Ok(deleted) => crate::stashes::purge_deleted_stash_files(&db, &deleted),
Err(e) => println!("Failed to delete context: {}", e),
}
Ok(())
}
Expand Down
Loading
Loading