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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ tests/data
.vimspector.json
uv.lock
.python-version
venv/
91 changes: 91 additions & 0 deletions src/apyanki/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -443,6 +443,97 @@ def review(query: str, check_markdown_consistency: bool, cmc_range: int) -> None
i += 1


@main.command()
@click.argument("query", nargs=-1, required=True)
@click.option(
"--force-multiple",
"-f",
is_flag=True,
help="Allow editing multiple notes (will edit them one by one)",
)
def edit(query: str, force_multiple: bool) -> None:
"""Edit notes that match QUERY directly.

This command allows direct editing of notes matching the provided query
without navigating through the interactive review interface.

If the query matches multiple notes, you'll be prompted to confirm
unless --force-multiple is specified.

Examples:

\b
# Edit a note by its card ID
apy edit cid:1740342619916

\b
# Edit a note by its note ID
apy edit nid:1234567890123

\b
# Edit a note containing specific text
apy edit "front:error"
"""
query = " ".join(query)

with Anki(**cfg) as a:
notes = list(a.find_notes(query))

# Handle no matches
if not notes:
console.print(f"No notes found matching query: {query}")
return

# Handle multiple matches
if len(notes) > 1 and not force_multiple:
console.print(f"Query matched {len(notes)} notes. The first five:\n")

# Show preview of the first 5 matching notes
for i, note in enumerate(notes[:5]):
preview_text = note.n.fields[0][:50].replace("\n", " ")
if len(preview_text) == 50:
preview_text += "..."
console.print(f"{i+1}. nid:{note.n.id} - {preview_text}")

console.print(
"\nHints:\n"
"* Use 'apy edit --force-multiple' to edit all matches or refine your query so it only matches a single note.\n"
"* Use 'apy list QUERY' to view all matches."
)
return

# Edit each note
edited_count = 0
for i, note in enumerate(notes):
if len(notes) > 1:
console.print(
f"\nEditing note {i+1} of {len(notes)} (nid: {note.n.id})"
)

# Show a brief preview of the note
preview_text = note.n.fields[0][:50].replace("\n", " ")
if len(preview_text) == 50:
preview_text += "..."
console.print(f"Content preview: {preview_text}")
console.print(f"Tags: {', '.join(note.n.tags)}")

if not console.confirm("Edit this note?"):
console.print("Skipping...")
continue

# Use the direct edit method (bypassing the review interface)
note.edit()
edited_count += 1

# Summary message
if edited_count > 0:
console.print(
f"\n[green]Successfully edited {edited_count} note(s)[/green]"
)
else:
console.print("\n[yellow]No notes were edited[/yellow]")


@main.command()
def sync() -> None:
"""Synchronize collection with AnkiWeb."""
Expand Down
3 changes: 2 additions & 1 deletion uv.lock

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