bouquin 0.1.12__py3-none-any.whl → 0.2.1.3__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.
- bouquin/db.py +4 -41
- bouquin/find_bar.py +33 -11
- bouquin/history_dialog.py +27 -26
- bouquin/main_window.py +495 -120
- bouquin/markdown_editor.py +920 -0
- bouquin/search.py +46 -30
- bouquin/toolbar.py +0 -42
- {bouquin-0.1.12.dist-info → bouquin-0.2.1.3.dist-info}/METADATA +5 -7
- bouquin-0.2.1.3.dist-info/RECORD +21 -0
- bouquin/editor.py +0 -1009
- bouquin-0.1.12.dist-info/RECORD +0 -21
- {bouquin-0.1.12.dist-info → bouquin-0.2.1.3.dist-info}/LICENSE +0 -0
- {bouquin-0.1.12.dist-info → bouquin-0.2.1.3.dist-info}/WHEEL +0 -0
- {bouquin-0.1.12.dist-info → bouquin-0.2.1.3.dist-info}/entry_points.txt +0 -0
bouquin/search.py
CHANGED
|
@@ -4,7 +4,6 @@ import re
|
|
|
4
4
|
from typing import Iterable, Tuple
|
|
5
5
|
|
|
6
6
|
from PySide6.QtCore import Qt, Signal
|
|
7
|
-
from PySide6.QtGui import QFont, QTextCharFormat, QTextCursor, QTextDocument
|
|
8
7
|
from PySide6.QtWidgets import (
|
|
9
8
|
QFrame,
|
|
10
9
|
QLabel,
|
|
@@ -149,10 +148,12 @@ class Search(QWidget):
|
|
|
149
148
|
self.results.setItemWidget(item, container)
|
|
150
149
|
|
|
151
150
|
# --- Snippet/highlight helpers -----------------------------------------
|
|
152
|
-
def _make_html_snippet(
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
151
|
+
def _make_html_snippet(
|
|
152
|
+
self, markdown_src: str, query: str, *, radius=60, maxlen=180
|
|
153
|
+
):
|
|
154
|
+
# For markdown, we can work directly with the text
|
|
155
|
+
# Strip markdown formatting for display
|
|
156
|
+
plain = self._strip_markdown(markdown_src)
|
|
156
157
|
if not plain:
|
|
157
158
|
return "", False, False
|
|
158
159
|
|
|
@@ -179,30 +180,45 @@ class Search(QWidget):
|
|
|
179
180
|
start = max(0, min(idx - radius, max(0, L - maxlen)))
|
|
180
181
|
end = min(L, max(idx + mlen + radius, start + maxlen))
|
|
181
182
|
|
|
182
|
-
#
|
|
183
|
+
# Extract snippet and highlight matches
|
|
184
|
+
snippet = plain[start:end]
|
|
185
|
+
|
|
186
|
+
# Escape HTML and bold matches
|
|
187
|
+
import html as _html
|
|
188
|
+
|
|
189
|
+
snippet_html = _html.escape(snippet)
|
|
183
190
|
if tokens:
|
|
184
|
-
lower = plain.lower()
|
|
185
|
-
fmt = QTextCharFormat()
|
|
186
|
-
fmt.setFontWeight(QFont.Weight.Bold)
|
|
187
191
|
for t in tokens:
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
#
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
192
|
+
# Case-insensitive replacement
|
|
193
|
+
pattern = re.compile(re.escape(t), re.IGNORECASE)
|
|
194
|
+
snippet_html = pattern.sub(
|
|
195
|
+
lambda m: f"<b>{m.group(0)}</b>", snippet_html
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
return snippet_html, start > 0, end < L
|
|
199
|
+
|
|
200
|
+
def _strip_markdown(self, markdown: str) -> str:
|
|
201
|
+
"""Strip markdown formatting for plain text display."""
|
|
202
|
+
# Remove images
|
|
203
|
+
text = re.sub(r"!\[.*?\]\(.*?\)", "[Image]", markdown)
|
|
204
|
+
# Remove links but keep text
|
|
205
|
+
text = re.sub(r"\[([^\]]+)\]\([^\)]+\)", r"\1", text)
|
|
206
|
+
# Remove inline code backticks
|
|
207
|
+
text = re.sub(r"`([^`]+)`", r"\1", text)
|
|
208
|
+
# Remove bold/italic markers
|
|
209
|
+
text = re.sub(r"\*\*([^*]+)\*\*", r"\1", text)
|
|
210
|
+
text = re.sub(r"__([^_]+)__", r"\1", text)
|
|
211
|
+
text = re.sub(r"\*([^*]+)\*", r"\1", text)
|
|
212
|
+
text = re.sub(r"_([^_]+)_", r"\1", text)
|
|
213
|
+
# Remove strikethrough
|
|
214
|
+
text = re.sub(r"~~([^~]+)~~", r"\1", text)
|
|
215
|
+
# Remove heading markers
|
|
216
|
+
text = re.sub(r"^#{1,6}\s+", "", text, flags=re.MULTILINE)
|
|
217
|
+
# Remove list markers
|
|
218
|
+
text = re.sub(r"^\s*[-*+]\s+", "", text, flags=re.MULTILINE)
|
|
219
|
+
text = re.sub(r"^\s*\d+\.\s+", "", text, flags=re.MULTILINE)
|
|
220
|
+
# Remove checkbox markers
|
|
221
|
+
text = re.sub(r"^\s*-\s*\[[x ☐☑]\]\s+", "", text, flags=re.MULTILINE)
|
|
222
|
+
# Remove code block fences
|
|
223
|
+
text = re.sub(r"```[^\n]*\n", "", text)
|
|
224
|
+
return text.strip()
|
bouquin/toolbar.py
CHANGED
|
@@ -8,14 +8,12 @@ from PySide6.QtWidgets import QToolBar
|
|
|
8
8
|
class ToolBar(QToolBar):
|
|
9
9
|
boldRequested = Signal()
|
|
10
10
|
italicRequested = Signal()
|
|
11
|
-
underlineRequested = Signal()
|
|
12
11
|
strikeRequested = Signal()
|
|
13
12
|
codeRequested = Signal()
|
|
14
13
|
headingRequested = Signal(int)
|
|
15
14
|
bulletsRequested = Signal()
|
|
16
15
|
numbersRequested = Signal()
|
|
17
16
|
checkboxesRequested = Signal()
|
|
18
|
-
alignRequested = Signal(Qt.AlignmentFlag)
|
|
19
17
|
historyRequested = Signal()
|
|
20
18
|
insertImageRequested = Signal()
|
|
21
19
|
|
|
@@ -39,12 +37,6 @@ class ToolBar(QToolBar):
|
|
|
39
37
|
self.actItalic.setShortcut(QKeySequence.Italic)
|
|
40
38
|
self.actItalic.triggered.connect(self.italicRequested)
|
|
41
39
|
|
|
42
|
-
self.actUnderline = QAction("U", self)
|
|
43
|
-
self.actUnderline.setToolTip("Underline")
|
|
44
|
-
self.actUnderline.setCheckable(True)
|
|
45
|
-
self.actUnderline.setShortcut(QKeySequence.Underline)
|
|
46
|
-
self.actUnderline.triggered.connect(self.underlineRequested)
|
|
47
|
-
|
|
48
40
|
self.actStrike = QAction("S", self)
|
|
49
41
|
self.actStrike.setToolTip("Strikethrough")
|
|
50
42
|
self.actStrike.setCheckable(True)
|
|
@@ -97,24 +89,6 @@ class ToolBar(QToolBar):
|
|
|
97
89
|
self.actInsertImg.setShortcut("Ctrl+Shift+I")
|
|
98
90
|
self.actInsertImg.triggered.connect(self.insertImageRequested)
|
|
99
91
|
|
|
100
|
-
# Alignment
|
|
101
|
-
self.actAlignL = QAction("L", self)
|
|
102
|
-
self.actAlignL.setToolTip("Align Left")
|
|
103
|
-
self.actAlignL.setCheckable(True)
|
|
104
|
-
self.actAlignL.triggered.connect(lambda: self.alignRequested.emit(Qt.AlignLeft))
|
|
105
|
-
self.actAlignC = QAction("C", self)
|
|
106
|
-
self.actAlignC.setToolTip("Align Center")
|
|
107
|
-
self.actAlignC.setCheckable(True)
|
|
108
|
-
self.actAlignC.triggered.connect(
|
|
109
|
-
lambda: self.alignRequested.emit(Qt.AlignHCenter)
|
|
110
|
-
)
|
|
111
|
-
self.actAlignR = QAction("R", self)
|
|
112
|
-
self.actAlignR.setToolTip("Align Right")
|
|
113
|
-
self.actAlignR.setCheckable(True)
|
|
114
|
-
self.actAlignR.triggered.connect(
|
|
115
|
-
lambda: self.alignRequested.emit(Qt.AlignRight)
|
|
116
|
-
)
|
|
117
|
-
|
|
118
92
|
# History button
|
|
119
93
|
self.actHistory = QAction("History", self)
|
|
120
94
|
self.actHistory.triggered.connect(self.historyRequested)
|
|
@@ -125,7 +99,6 @@ class ToolBar(QToolBar):
|
|
|
125
99
|
for a in (
|
|
126
100
|
self.actBold,
|
|
127
101
|
self.actItalic,
|
|
128
|
-
self.actUnderline,
|
|
129
102
|
self.actStrike,
|
|
130
103
|
self.actH1,
|
|
131
104
|
self.actH2,
|
|
@@ -135,11 +108,6 @@ class ToolBar(QToolBar):
|
|
|
135
108
|
a.setCheckable(True)
|
|
136
109
|
a.setActionGroup(self.grpHeadings)
|
|
137
110
|
|
|
138
|
-
self.grpAlign = QActionGroup(self)
|
|
139
|
-
self.grpAlign.setExclusive(True)
|
|
140
|
-
for a in (self.actAlignL, self.actAlignC, self.actAlignR):
|
|
141
|
-
a.setActionGroup(self.grpAlign)
|
|
142
|
-
|
|
143
111
|
self.grpLists = QActionGroup(self)
|
|
144
112
|
self.grpLists.setExclusive(True)
|
|
145
113
|
for a in (self.actBullets, self.actNumbers, self.actCheckboxes):
|
|
@@ -150,7 +118,6 @@ class ToolBar(QToolBar):
|
|
|
150
118
|
[
|
|
151
119
|
self.actBold,
|
|
152
120
|
self.actItalic,
|
|
153
|
-
self.actUnderline,
|
|
154
121
|
self.actStrike,
|
|
155
122
|
self.actCode,
|
|
156
123
|
self.actH1,
|
|
@@ -161,9 +128,6 @@ class ToolBar(QToolBar):
|
|
|
161
128
|
self.actNumbers,
|
|
162
129
|
self.actCheckboxes,
|
|
163
130
|
self.actInsertImg,
|
|
164
|
-
self.actAlignL,
|
|
165
|
-
self.actAlignC,
|
|
166
|
-
self.actAlignR,
|
|
167
131
|
self.actHistory,
|
|
168
132
|
]
|
|
169
133
|
)
|
|
@@ -171,7 +135,6 @@ class ToolBar(QToolBar):
|
|
|
171
135
|
def _apply_toolbar_styles(self):
|
|
172
136
|
self._style_letter_button(self.actBold, "B", bold=True)
|
|
173
137
|
self._style_letter_button(self.actItalic, "I", italic=True)
|
|
174
|
-
self._style_letter_button(self.actUnderline, "U", underline=True)
|
|
175
138
|
self._style_letter_button(self.actStrike, "S", strike=True)
|
|
176
139
|
# Monospace look for code; use a fixed font
|
|
177
140
|
code_font = QFontDatabase.systemFont(QFontDatabase.FixedFont)
|
|
@@ -187,11 +150,6 @@ class ToolBar(QToolBar):
|
|
|
187
150
|
self._style_letter_button(self.actBullets, "•")
|
|
188
151
|
self._style_letter_button(self.actNumbers, "1.")
|
|
189
152
|
|
|
190
|
-
# Alignment
|
|
191
|
-
self._style_letter_button(self.actAlignL, "L")
|
|
192
|
-
self._style_letter_button(self.actAlignC, "C")
|
|
193
|
-
self._style_letter_button(self.actAlignR, "R")
|
|
194
|
-
|
|
195
153
|
# History
|
|
196
154
|
self._style_letter_button(self.actHistory, "View History")
|
|
197
155
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: bouquin
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.2.1.3
|
|
4
4
|
Summary: Bouquin is a simple, opinionated notebook application written in Python, PyQt and SQLCipher.
|
|
5
5
|
Home-page: https://git.mig5.net/mig5/bouquin
|
|
6
6
|
License: GPL-3.0-or-later
|
|
@@ -13,7 +13,6 @@ Classifier: Programming Language :: Python :: 3.9
|
|
|
13
13
|
Classifier: Programming Language :: Python :: 3.10
|
|
14
14
|
Classifier: Programming Language :: Python :: 3.11
|
|
15
15
|
Classifier: Programming Language :: Python :: 3.12
|
|
16
|
-
Requires-Dist: markdownify (>=1.2.0,<2.0.0)
|
|
17
16
|
Requires-Dist: pyside6 (>=6.8.1,<7.0.0)
|
|
18
17
|
Requires-Dist: sqlcipher3-wheels (>=0.5.5.post0,<0.6.0)
|
|
19
18
|
Project-URL: Repository, https://git.mig5.net/mig5/bouquin
|
|
@@ -36,9 +35,7 @@ There is deliberately no network connectivity or syncing intended.
|
|
|
36
35
|
|
|
37
36
|
## Screenshot
|
|
38
37
|
|
|
39
|
-

|
|
38
|
+

|
|
42
39
|
|
|
43
40
|
## Features
|
|
44
41
|
|
|
@@ -46,9 +43,10 @@ There is deliberately no network connectivity or syncing intended.
|
|
|
46
43
|
* Encryption key is prompted for and never stored, unless user chooses to via Settings
|
|
47
44
|
* Every 'page' is linked to the calendar day
|
|
48
45
|
* All changes are version controlled, with ability to view/diff versions and revert
|
|
49
|
-
* Text is
|
|
46
|
+
* Text is Markdown with basic styling
|
|
47
|
+
* Tabs are supported - right-click on a date from the calendar to open it in a new tab.
|
|
50
48
|
* Images are supported
|
|
51
|
-
* Search
|
|
49
|
+
* Search all pages, or find text on page (Ctrl+F)
|
|
52
50
|
* Automatic periodic saving (or explicitly save)
|
|
53
51
|
* Transparent integrity checking of the database when it opens
|
|
54
52
|
* Automatic locking of the app after a period of inactivity (default 15 min)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
bouquin/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
2
|
+
bouquin/__main__.py,sha256=Vdhw8YA1K3wPMlbJQYL5WqvRzAKVeZ16mZQFO9VRmCo,62
|
|
3
|
+
bouquin/db.py,sha256=mx7ogJnH_wVaCRpkOy7KBLLHyMOJDorHY14L9xz_QkE,15576
|
|
4
|
+
bouquin/find_bar.py,sha256=oDl3XyW7NY6q-MvDCuGglH62-nLER_HEi0W2blbWF0Y,6335
|
|
5
|
+
bouquin/history_dialog.py,sha256=m-crhYV_a3CMGh5-sjhvIOqpFdmB3VKNr5QCoiEaDHY,6259
|
|
6
|
+
bouquin/key_prompt.py,sha256=oQhLDOQv1QUr_ImA9Zu78JkDpVqPbZZJdhu0c_5Cq5U,1266
|
|
7
|
+
bouquin/lock_overlay.py,sha256=d1xoBMx2CSNk0zP5V6k65UqJCC4aiIrwNlfDld49ymA,4197
|
|
8
|
+
bouquin/main.py,sha256=lBOMS7THgHb4CAJVj8NRYABtNAEez9jlL0wI1oOtfT4,611
|
|
9
|
+
bouquin/main_window.py,sha256=idyUFj_lNlmSCZgfYecV16JMzQJAm2ZcmEmzNCM-jmQ,50690
|
|
10
|
+
bouquin/markdown_editor.py,sha256=5esRhZuUEKySw2YjVPaeXOA0TOJW13OaEK1h9I_ZDdw,33318
|
|
11
|
+
bouquin/save_dialog.py,sha256=YUkZ8kL1hja15D8qv68yY2zPyjBAJZsDQbp6Y6EvDbA,1023
|
|
12
|
+
bouquin/search.py,sha256=jUhiy90pThmQUvsDnKYjes5SRnAWKBkqGpHPWMsfRJs,7764
|
|
13
|
+
bouquin/settings.py,sha256=F3WLkk2G_By3ppZsRbrnq3PtL2Zav7aA-mIegvGTc8Y,1128
|
|
14
|
+
bouquin/settings_dialog.py,sha256=YQHYjn3y2sgJGtkkApADxAodojDfLvnZYeQmynXLkos,10699
|
|
15
|
+
bouquin/theme.py,sha256=6ODq9oKLAk7lnvW9uGRMIIjfhf70SgPivLYh3ZN671M,3489
|
|
16
|
+
bouquin/toolbar.py,sha256=_FZ4lqJmQD86qx-0k7XpAu3HzyFedX2pG2mGyug0ea8,6588
|
|
17
|
+
bouquin-0.2.1.3.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
18
|
+
bouquin-0.2.1.3.dist-info/METADATA,sha256=S2vVxGNzrff0_pLwOBfzXjIR3DEWG0Fhjt0IVFzNt-8,3141
|
|
19
|
+
bouquin-0.2.1.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
20
|
+
bouquin-0.2.1.3.dist-info/entry_points.txt,sha256=d2C5Mc85suj1vWg_mmcfFuEBAYEkdwhZquusme5EWuQ,49
|
|
21
|
+
bouquin-0.2.1.3.dist-info/RECORD,,
|