notesctl 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- notesctl/__init__.py +3 -0
- notesctl/cli.py +285 -0
- notesctl/converter/__init__.py +5 -0
- notesctl/converter/markdown.py +232 -0
- notesctl/database/__init__.py +7 -0
- notesctl/database/connection.py +156 -0
- notesctl/database/models.py +74 -0
- notesctl/database/queries.py +241 -0
- notesctl/exporter.py +245 -0
- notesctl/parser/__init__.py +5 -0
- notesctl/parser/note_parser.py +227 -0
- notesctl/parser/protobuf/__init__.py +5 -0
- notesctl/parser/protobuf/notestore_pb2.py +49 -0
- notesctl-1.0.0.dist-info/METADATA +113 -0
- notesctl-1.0.0.dist-info/RECORD +18 -0
- notesctl-1.0.0.dist-info/WHEEL +4 -0
- notesctl-1.0.0.dist-info/entry_points.txt +2 -0
- notesctl-1.0.0.dist-info/licenses/LICENSE +373 -0
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
"""Parser for Apple Notes gzip-compressed protobuf content."""
|
|
2
|
+
|
|
3
|
+
import gzip
|
|
4
|
+
from dataclasses import dataclass, field
|
|
5
|
+
from enum import IntEnum
|
|
6
|
+
|
|
7
|
+
from .protobuf.notestore_pb2 import NoteStoreProto
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class StyleType(IntEnum):
|
|
11
|
+
"""Paragraph style types in Apple Notes."""
|
|
12
|
+
|
|
13
|
+
TITLE = 0
|
|
14
|
+
HEADING = 1
|
|
15
|
+
SUBHEADING = 2
|
|
16
|
+
MONOSPACED = 4
|
|
17
|
+
DOTTED_LIST = 100 # Bullet list
|
|
18
|
+
DASHED_LIST = 101
|
|
19
|
+
NUMBERED_LIST = 102
|
|
20
|
+
CHECKLIST = 103
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class TextRun:
|
|
25
|
+
"""A run of text with consistent formatting."""
|
|
26
|
+
|
|
27
|
+
text: str
|
|
28
|
+
bold: bool = False
|
|
29
|
+
italic: bool = False
|
|
30
|
+
underline: bool = False
|
|
31
|
+
strikethrough: bool = False
|
|
32
|
+
monospace: bool = False
|
|
33
|
+
link: str | None = None
|
|
34
|
+
attachment_id: str | None = None
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@dataclass
|
|
38
|
+
class Paragraph:
|
|
39
|
+
"""A paragraph with its style and text runs."""
|
|
40
|
+
|
|
41
|
+
runs: list[TextRun] = field(default_factory=list)
|
|
42
|
+
style: StyleType | None = None
|
|
43
|
+
indent: int = 0
|
|
44
|
+
is_checklist: bool = False
|
|
45
|
+
checklist_checked: bool = False
|
|
46
|
+
is_blockquote: bool = False
|
|
47
|
+
list_item_number: int | None = None # For numbered lists
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def plain_text(self) -> str:
|
|
51
|
+
"""Get plain text content without formatting."""
|
|
52
|
+
return "".join(run.text for run in self.runs)
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@dataclass
|
|
56
|
+
class ParsedNote:
|
|
57
|
+
"""Parsed note content ready for conversion."""
|
|
58
|
+
|
|
59
|
+
title: str
|
|
60
|
+
paragraphs: list[Paragraph] = field(default_factory=list)
|
|
61
|
+
attachment_ids: list[str] = field(default_factory=list)
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def plain_text(self) -> str:
|
|
65
|
+
"""Get full plain text content."""
|
|
66
|
+
return "\n".join(p.plain_text for p in self.paragraphs)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class NoteParser:
|
|
70
|
+
"""Parser for Apple Notes protobuf content."""
|
|
71
|
+
|
|
72
|
+
def __init__(self):
|
|
73
|
+
"""Initialize the parser."""
|
|
74
|
+
self._numbered_list_counters: dict[int, int] = {}
|
|
75
|
+
|
|
76
|
+
def parse(self, data: bytes, title: str = "Untitled") -> ParsedNote:
|
|
77
|
+
"""
|
|
78
|
+
Parse gzip-compressed protobuf note data.
|
|
79
|
+
|
|
80
|
+
Args:
|
|
81
|
+
data: Gzip-compressed protobuf data
|
|
82
|
+
title: Note title (from database)
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
ParsedNote with parsed content
|
|
86
|
+
"""
|
|
87
|
+
if not data:
|
|
88
|
+
return ParsedNote(title=title)
|
|
89
|
+
|
|
90
|
+
# Decompress gzip data
|
|
91
|
+
try:
|
|
92
|
+
decompressed = gzip.decompress(data)
|
|
93
|
+
except gzip.BadGzipFile:
|
|
94
|
+
# Data might not be gzipped, try raw protobuf
|
|
95
|
+
decompressed = data
|
|
96
|
+
|
|
97
|
+
# Parse protobuf
|
|
98
|
+
try:
|
|
99
|
+
proto = NoteStoreProto()
|
|
100
|
+
proto.ParseFromString(decompressed)
|
|
101
|
+
except Exception:
|
|
102
|
+
# If parsing fails, return empty note
|
|
103
|
+
return ParsedNote(title=title)
|
|
104
|
+
|
|
105
|
+
return self._parse_proto(proto, title)
|
|
106
|
+
|
|
107
|
+
def _parse_proto(self, proto: NoteStoreProto, title: str) -> ParsedNote:
|
|
108
|
+
"""Parse the protobuf message into structured content."""
|
|
109
|
+
note = ParsedNote(title=title)
|
|
110
|
+
|
|
111
|
+
if not proto.HasField("document") or not proto.document.HasField("note"):
|
|
112
|
+
return note
|
|
113
|
+
|
|
114
|
+
note_proto = proto.document.note
|
|
115
|
+
text = note_proto.note_text
|
|
116
|
+
|
|
117
|
+
# Reset numbered list counters for this note
|
|
118
|
+
self._numbered_list_counters = {}
|
|
119
|
+
|
|
120
|
+
# Process attribute runs
|
|
121
|
+
position = 0
|
|
122
|
+
current_paragraph = Paragraph()
|
|
123
|
+
paragraphs: list[Paragraph] = []
|
|
124
|
+
|
|
125
|
+
for attr_run in note_proto.attribute_run:
|
|
126
|
+
run_length = attr_run.length
|
|
127
|
+
run_text = text[position : position + run_length]
|
|
128
|
+
position += run_length
|
|
129
|
+
|
|
130
|
+
# Get paragraph style if present
|
|
131
|
+
style = None
|
|
132
|
+
indent = 0
|
|
133
|
+
is_checklist = False
|
|
134
|
+
checklist_checked = False
|
|
135
|
+
is_blockquote = False
|
|
136
|
+
|
|
137
|
+
if attr_run.HasField("paragraph_style"):
|
|
138
|
+
ps = attr_run.paragraph_style
|
|
139
|
+
if ps.HasField("style_type"):
|
|
140
|
+
try:
|
|
141
|
+
style = StyleType(ps.style_type)
|
|
142
|
+
except ValueError:
|
|
143
|
+
pass
|
|
144
|
+
|
|
145
|
+
if ps.HasField("indent_amount"):
|
|
146
|
+
indent = ps.indent_amount
|
|
147
|
+
|
|
148
|
+
if ps.HasField("checklist"):
|
|
149
|
+
is_checklist = True
|
|
150
|
+
if ps.checklist.HasField("done"):
|
|
151
|
+
checklist_checked = bool(ps.checklist.done)
|
|
152
|
+
|
|
153
|
+
if ps.HasField("block_quote"):
|
|
154
|
+
is_blockquote = bool(ps.block_quote)
|
|
155
|
+
|
|
156
|
+
# Get font hints for italic detection
|
|
157
|
+
is_italic = False
|
|
158
|
+
if attr_run.HasField("font"):
|
|
159
|
+
# Font hints: 1 = italic
|
|
160
|
+
if attr_run.font.HasField("font_hints"):
|
|
161
|
+
is_italic = (attr_run.font.font_hints & 1) != 0
|
|
162
|
+
|
|
163
|
+
# Create text run with formatting
|
|
164
|
+
text_run = TextRun(
|
|
165
|
+
text=run_text,
|
|
166
|
+
bold=attr_run.HasField("font_weight") and attr_run.font_weight > 0,
|
|
167
|
+
italic=is_italic,
|
|
168
|
+
underline=attr_run.HasField("underlined") and attr_run.underlined > 0,
|
|
169
|
+
strikethrough=attr_run.HasField("strikethrough") and attr_run.strikethrough > 0,
|
|
170
|
+
monospace=style == StyleType.MONOSPACED if style else False,
|
|
171
|
+
link=attr_run.link if attr_run.HasField("link") else None,
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# Check for attachment
|
|
175
|
+
if attr_run.HasField("attachment_info"):
|
|
176
|
+
if attr_run.attachment_info.HasField("attachment_identifier"):
|
|
177
|
+
att_id = attr_run.attachment_info.attachment_identifier
|
|
178
|
+
text_run.attachment_id = att_id
|
|
179
|
+
note.attachment_ids.append(att_id)
|
|
180
|
+
|
|
181
|
+
# Split by newlines to create paragraphs
|
|
182
|
+
lines = run_text.split("\n")
|
|
183
|
+
for i, line in enumerate(lines):
|
|
184
|
+
if i > 0:
|
|
185
|
+
# New paragraph on newline
|
|
186
|
+
if current_paragraph.runs or current_paragraph.style:
|
|
187
|
+
paragraphs.append(current_paragraph)
|
|
188
|
+
current_paragraph = Paragraph()
|
|
189
|
+
|
|
190
|
+
if line or i == len(lines) - 1:
|
|
191
|
+
line_run = TextRun(
|
|
192
|
+
text=line,
|
|
193
|
+
bold=text_run.bold,
|
|
194
|
+
italic=text_run.italic,
|
|
195
|
+
underline=text_run.underline,
|
|
196
|
+
strikethrough=text_run.strikethrough,
|
|
197
|
+
monospace=text_run.monospace,
|
|
198
|
+
link=text_run.link,
|
|
199
|
+
attachment_id=text_run.attachment_id if i == 0 else None,
|
|
200
|
+
)
|
|
201
|
+
current_paragraph.runs.append(line_run)
|
|
202
|
+
|
|
203
|
+
# Apply paragraph style to first line of this run
|
|
204
|
+
if i == 0:
|
|
205
|
+
if style is not None:
|
|
206
|
+
current_paragraph.style = style
|
|
207
|
+
current_paragraph.indent = indent
|
|
208
|
+
current_paragraph.is_checklist = is_checklist
|
|
209
|
+
current_paragraph.checklist_checked = checklist_checked
|
|
210
|
+
current_paragraph.is_blockquote = is_blockquote
|
|
211
|
+
|
|
212
|
+
# Track numbered list items
|
|
213
|
+
if style == StyleType.NUMBERED_LIST:
|
|
214
|
+
counter_key = indent
|
|
215
|
+
self._numbered_list_counters[counter_key] = (
|
|
216
|
+
self._numbered_list_counters.get(counter_key, 0) + 1
|
|
217
|
+
)
|
|
218
|
+
current_paragraph.list_item_number = self._numbered_list_counters[
|
|
219
|
+
counter_key
|
|
220
|
+
]
|
|
221
|
+
|
|
222
|
+
# Add final paragraph
|
|
223
|
+
if current_paragraph.runs:
|
|
224
|
+
paragraphs.append(current_paragraph)
|
|
225
|
+
|
|
226
|
+
note.paragraphs = paragraphs
|
|
227
|
+
return note
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Generated by the protocol buffer compiler. DO NOT EDIT!
|
|
3
|
+
# NO CHECKED-IN PROTOBUF GENCODE
|
|
4
|
+
# source: notestore.proto
|
|
5
|
+
# Protobuf Python Version: 6.33.4
|
|
6
|
+
"""Generated protocol buffer code."""
|
|
7
|
+
|
|
8
|
+
from google.protobuf import descriptor as _descriptor
|
|
9
|
+
from google.protobuf import descriptor_pool as _descriptor_pool
|
|
10
|
+
from google.protobuf import runtime_version as _runtime_version
|
|
11
|
+
from google.protobuf import symbol_database as _symbol_database
|
|
12
|
+
from google.protobuf.internal import builder as _builder
|
|
13
|
+
|
|
14
|
+
_runtime_version.ValidateProtobufRuntimeVersion(
|
|
15
|
+
_runtime_version.Domain.PUBLIC, 6, 33, 4, "", "notestore.proto"
|
|
16
|
+
)
|
|
17
|
+
# @@protoc_insertion_point(imports)
|
|
18
|
+
|
|
19
|
+
_sym_db = _symbol_database.Default()
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(
|
|
23
|
+
b'\n\x0fnotestore.proto\x12\tnotestore"7\n\x0eNoteStoreProto\x12%\n\x08\x64ocument\x18\x02 \x02(\x0b\x32\x13.notestore.Document")\n\x08\x44ocument\x12\x1d\n\x04note\x18\x03 \x02(\x0b\x32\x0f.notestore.Note"I\n\x04Note\x12\x11\n\tnote_text\x18\x02 \x02(\t\x12.\n\rattribute_run\x18\x05 \x03(\x0b\x32\x17.notestore.AttributeRun"\xa9\x02\n\x0c\x41ttributeRun\x12\x0e\n\x06length\x18\x01 \x02(\x05\x12\x32\n\x0fparagraph_style\x18\x02 \x01(\x0b\x32\x19.notestore.ParagraphStyle\x12\x1d\n\x04\x66ont\x18\x03 \x01(\x0b\x32\x0f.notestore.Font\x12\x13\n\x0b\x66ont_weight\x18\x05 \x01(\x05\x12\x12\n\nunderlined\x18\x06 \x01(\x05\x12\x15\n\rstrikethrough\x18\x07 \x01(\x05\x12\x13\n\x0bsuperscript\x18\x08 \x01(\x05\x12\x0c\n\x04link\x18\t \x01(\t\x12\x1f\n\x05\x63olor\x18\n \x01(\x0b\x32\x10.notestore.Color\x12\x32\n\x0f\x61ttachment_info\x18\x0c \x01(\x0b\x32\x19.notestore.AttachmentInfo"\x8c\x01\n\x0eParagraphStyle\x12\x12\n\nstyle_type\x18\x01 \x01(\x05\x12\x11\n\talignment\x18\x02 \x01(\x05\x12\x15\n\rindent_amount\x18\x04 \x01(\x05\x12\'\n\tchecklist\x18\x05 \x01(\x0b\x32\x14.notestore.Checklist\x12\x13\n\x0b\x62lock_quote\x18\x06 \x01(\x05"\'\n\tChecklist\x12\x0c\n\x04uuid\x18\x01 \x01(\x0c\x12\x0c\n\x04\x64one\x18\x02 \x01(\x05"A\n\x04\x46ont\x12\x11\n\tfont_name\x18\x01 \x01(\t\x12\x12\n\npoint_size\x18\x02 \x01(\x02\x12\x12\n\nfont_hints\x18\x03 \x01(\x05"@\n\x05\x43olor\x12\x0b\n\x03red\x18\x01 \x01(\x02\x12\r\n\x05green\x18\x02 \x01(\x02\x12\x0c\n\x04\x62lue\x18\x03 \x01(\x02\x12\r\n\x05\x61lpha\x18\x04 \x01(\x02"A\n\x0e\x41ttachmentInfo\x12\x1d\n\x15\x61ttachment_identifier\x18\x01 \x01(\t\x12\x10\n\x08type_uti\x18\x02 \x01(\t'
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
_globals = globals()
|
|
27
|
+
_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, _globals)
|
|
28
|
+
_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, "notestore_pb2", _globals)
|
|
29
|
+
if not _descriptor._USE_C_DESCRIPTORS:
|
|
30
|
+
DESCRIPTOR._loaded_options = None
|
|
31
|
+
_globals["_NOTESTOREPROTO"]._serialized_start = 30
|
|
32
|
+
_globals["_NOTESTOREPROTO"]._serialized_end = 85
|
|
33
|
+
_globals["_DOCUMENT"]._serialized_start = 87
|
|
34
|
+
_globals["_DOCUMENT"]._serialized_end = 128
|
|
35
|
+
_globals["_NOTE"]._serialized_start = 130
|
|
36
|
+
_globals["_NOTE"]._serialized_end = 203
|
|
37
|
+
_globals["_ATTRIBUTERUN"]._serialized_start = 206
|
|
38
|
+
_globals["_ATTRIBUTERUN"]._serialized_end = 503
|
|
39
|
+
_globals["_PARAGRAPHSTYLE"]._serialized_start = 506
|
|
40
|
+
_globals["_PARAGRAPHSTYLE"]._serialized_end = 646
|
|
41
|
+
_globals["_CHECKLIST"]._serialized_start = 648
|
|
42
|
+
_globals["_CHECKLIST"]._serialized_end = 687
|
|
43
|
+
_globals["_FONT"]._serialized_start = 689
|
|
44
|
+
_globals["_FONT"]._serialized_end = 754
|
|
45
|
+
_globals["_COLOR"]._serialized_start = 756
|
|
46
|
+
_globals["_COLOR"]._serialized_end = 820
|
|
47
|
+
_globals["_ATTACHMENTINFO"]._serialized_start = 822
|
|
48
|
+
_globals["_ATTACHMENTINFO"]._serialized_end = 887
|
|
49
|
+
# @@protoc_insertion_point(module_scope)
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: notesctl
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Export Apple iCloud Notes to Markdown files safely
|
|
5
|
+
Project-URL: Homepage, https://github.com/jwmoss/notesctl
|
|
6
|
+
Project-URL: Repository, https://github.com/jwmoss/notesctl
|
|
7
|
+
Project-URL: Documentation, https://github.com/jwmoss/notesctl#readme
|
|
8
|
+
Project-URL: Issues, https://github.com/jwmoss/notesctl/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/jwmoss/notesctl/blob/main/CHANGELOG.md
|
|
10
|
+
License-Expression: MIT
|
|
11
|
+
License-File: LICENSE
|
|
12
|
+
Keywords: apple,export,icloud,markdown,notes
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
16
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
17
|
+
Classifier: Operating System :: MacOS
|
|
18
|
+
Classifier: Programming Language :: Python :: 3
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
22
|
+
Classifier: Topic :: Utilities
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.11
|
|
25
|
+
Requires-Dist: pillow>=10.0.0
|
|
26
|
+
Requires-Dist: protobuf>=4.25.0
|
|
27
|
+
Requires-Dist: rich>=13.0.0
|
|
28
|
+
Requires-Dist: typer>=0.9.0
|
|
29
|
+
Provides-Extra: dev
|
|
30
|
+
Requires-Dist: grpcio-tools>=1.60.0; extra == 'dev'
|
|
31
|
+
Requires-Dist: mypy>=1.0.0; extra == 'dev'
|
|
32
|
+
Requires-Dist: pytest-cov>=4.0.0; extra == 'dev'
|
|
33
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
34
|
+
Requires-Dist: ruff>=0.8.0; extra == 'dev'
|
|
35
|
+
Requires-Dist: types-pillow>=10.0.0; extra == 'dev'
|
|
36
|
+
Requires-Dist: types-protobuf>=4.24.0; extra == 'dev'
|
|
37
|
+
Description-Content-Type: text/markdown
|
|
38
|
+
|
|
39
|
+
# notesctl
|
|
40
|
+
|
|
41
|
+
Export Apple iCloud Notes to Markdown files safely.
|
|
42
|
+
|
|
43
|
+
## Features
|
|
44
|
+
|
|
45
|
+
- **Safe**: Copies database before reading, uses read-only mode
|
|
46
|
+
- **Complete**: Exports text, formatting, and attachments
|
|
47
|
+
- **Organized**: Groups notes by folder with YAML frontmatter
|
|
48
|
+
|
|
49
|
+
## Installation
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Using uv (recommended)
|
|
53
|
+
uv pip install .
|
|
54
|
+
|
|
55
|
+
# Or install in development mode
|
|
56
|
+
uv pip install -e ".[dev]"
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Usage
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
# Export all notes
|
|
63
|
+
uv run notesctl export -o ~/notes-backup
|
|
64
|
+
|
|
65
|
+
# Export specific folder
|
|
66
|
+
uv run notesctl export --folder "Work" -o ~/work-notes
|
|
67
|
+
|
|
68
|
+
# Preview without writing (safe)
|
|
69
|
+
uv run notesctl export --dry-run
|
|
70
|
+
|
|
71
|
+
# List notes
|
|
72
|
+
uv run notesctl list-notes
|
|
73
|
+
|
|
74
|
+
# List folders
|
|
75
|
+
uv run notesctl list-folders
|
|
76
|
+
|
|
77
|
+
# Show statistics
|
|
78
|
+
uv run notesctl stats
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
## Safety
|
|
82
|
+
|
|
83
|
+
This tool prioritizes safety:
|
|
84
|
+
|
|
85
|
+
1. **Copy-first**: Database files are copied to temp directory before reading
|
|
86
|
+
2. **Read-only**: SQLite connection uses URI mode=ro
|
|
87
|
+
3. **SELECT only**: All queries are validated to be SELECT statements
|
|
88
|
+
4. **Dry-run mode**: Preview exports without writing files
|
|
89
|
+
|
|
90
|
+
## Development
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# Install dev dependencies
|
|
94
|
+
uv pip install -e ".[dev]"
|
|
95
|
+
|
|
96
|
+
# Run tests
|
|
97
|
+
uv run pytest
|
|
98
|
+
|
|
99
|
+
# Run linter
|
|
100
|
+
uv run ruff check src tests
|
|
101
|
+
|
|
102
|
+
# Run type checker
|
|
103
|
+
uv run mypy src
|
|
104
|
+
|
|
105
|
+
# Format code
|
|
106
|
+
uv run ruff format src tests
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Requirements
|
|
110
|
+
|
|
111
|
+
- macOS with Apple Notes
|
|
112
|
+
- Python 3.11+
|
|
113
|
+
- uv (recommended) or pip
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
notesctl/__init__.py,sha256=X-odR8GWwfgOrw-lzonDw9SG4nu-u6EaQuHBf8NROTc,64
|
|
2
|
+
notesctl/cli.py,sha256=qnpGGG8e5CzN2H24ter_IfxrfzlDOIHIiGz12VyHkrk,7838
|
|
3
|
+
notesctl/exporter.py,sha256=ll0jUZSg_oXoZ1csZ1ypVO7mHkxfez5Fx6DjVrjnFf0,7711
|
|
4
|
+
notesctl/converter/__init__.py,sha256=uXGQKdQXqyLHZmvXwKL6vXEAW6vJzLAbI34SzbPpNCM,109
|
|
5
|
+
notesctl/converter/markdown.py,sha256=zT5oRPBwx_P_rvNgoi_qXUe-p-D0bey9T1cyXFYkRe8,8032
|
|
6
|
+
notesctl/database/__init__.py,sha256=Ku774zaf2ikCCEgxBT1EWR3BSmdGMkyb3Cd3ldIYOzw,264
|
|
7
|
+
notesctl/database/connection.py,sha256=g6w93I1ln5YjHt0FMSGT8u5aBw3mIknLeLswKOHYEfQ,5154
|
|
8
|
+
notesctl/database/models.py,sha256=EknKwfBoTqg_UNo_EjGGOGH1c_svQZPuC870uDuvUqo,2216
|
|
9
|
+
notesctl/database/queries.py,sha256=PloMefQqlL3J1y5iLeDYfEO1x08nvrWDhAOeyL_p8PM,7796
|
|
10
|
+
notesctl/parser/__init__.py,sha256=NGCner72eO9JJcf5BqRYeB-vPvjDDTpfLhBtnbBWoe8,127
|
|
11
|
+
notesctl/parser/note_parser.py,sha256=Vw0VbYSUcYNq4a_1kT-xwkjYID6g_fgEgPDE20TE6es,7726
|
|
12
|
+
notesctl/parser/protobuf/__init__.py,sha256=m876LJ-X14pTvjQMtViUYOCcOXGSLd3HZVQ2iTi_pWw,124
|
|
13
|
+
notesctl/parser/protobuf/notestore_pb2.py,sha256=gdPQL7rlbwbiV6_9lhFxj8kHhRo1Jeay-bEG4B7S5Ps,3614
|
|
14
|
+
notesctl-1.0.0.dist-info/METADATA,sha256=nFiXSZJr0xEPxCm2BFqU3_t5YZBNppRV2bIRmSEYt5Y,2927
|
|
15
|
+
notesctl-1.0.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
|
|
16
|
+
notesctl-1.0.0.dist-info/entry_points.txt,sha256=1Ebiz6QJFgcSPZ06R477NxNbc5J_smznlV_jrqsTxpE,46
|
|
17
|
+
notesctl-1.0.0.dist-info/licenses/LICENSE,sha256=Pz2eACSxkhsGfW9_iN60pgy-enjnbGTj8df8O3ebnQQ,16726
|
|
18
|
+
notesctl-1.0.0.dist-info/RECORD,,
|