bouquin 0.1.1__py3-none-any.whl → 0.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.

Potentially problematic release.


This version of bouquin might be problematic. Click here for more details.

bouquin/toolbar.py ADDED
@@ -0,0 +1,148 @@
1
+ from __future__ import annotations
2
+
3
+ from PySide6.QtCore import Signal, Qt
4
+ from PySide6.QtGui import QAction, QKeySequence, QFont, QFontDatabase
5
+ from PySide6.QtWidgets import QToolBar
6
+
7
+
8
+ class ToolBar(QToolBar):
9
+ boldRequested = Signal()
10
+ italicRequested = Signal()
11
+ underlineRequested = Signal()
12
+ strikeRequested = Signal()
13
+ codeRequested = Signal()
14
+ headingRequested = Signal(int)
15
+ bulletsRequested = Signal()
16
+ numbersRequested = Signal()
17
+ alignRequested = Signal(Qt.AlignmentFlag)
18
+
19
+ def __init__(self, parent=None):
20
+ super().__init__("Format", parent)
21
+ self.setObjectName("Format")
22
+ self.setToolButtonStyle(Qt.ToolButtonTextOnly)
23
+ self._build_actions()
24
+ self._apply_toolbar_styles()
25
+
26
+ def _build_actions(self):
27
+ self.actBold = QAction("Bold", self)
28
+ self.actBold.setShortcut(QKeySequence.Bold)
29
+ self.actBold.triggered.connect(self.boldRequested)
30
+
31
+ self.actItalic = QAction("Italic", self)
32
+ self.actItalic.setShortcut(QKeySequence.Italic)
33
+ self.actItalic.triggered.connect(self.italicRequested)
34
+
35
+ self.actUnderline = QAction("Underline", self)
36
+ self.actUnderline.setShortcut(QKeySequence.Underline)
37
+ self.actUnderline.triggered.connect(self.underlineRequested)
38
+
39
+ self.actStrike = QAction("Strikethrough", self)
40
+ self.actStrike.setShortcut("Ctrl+-")
41
+ self.actStrike.triggered.connect(self.strikeRequested)
42
+
43
+ self.actCode = QAction("Inline code", self)
44
+ self.actCode.setShortcut("Ctrl+`")
45
+ self.actCode.triggered.connect(self.codeRequested)
46
+
47
+ # Headings
48
+ self.actH1 = QAction("Heading 1", self)
49
+ self.actH2 = QAction("Heading 2", self)
50
+ self.actH3 = QAction("Heading 3", self)
51
+ self.actNormal = QAction("Normal text", self)
52
+ self.actH1.setShortcut("Ctrl+1")
53
+ self.actH2.setShortcut("Ctrl+2")
54
+ self.actH3.setShortcut("Ctrl+3")
55
+ self.actNormal.setShortcut("Ctrl+N")
56
+ self.actH1.triggered.connect(lambda: self.headingRequested.emit(24))
57
+ self.actH2.triggered.connect(lambda: self.headingRequested.emit(18))
58
+ self.actH3.triggered.connect(lambda: self.headingRequested.emit(14))
59
+ self.actNormal.triggered.connect(lambda: self.headingRequested.emit(0))
60
+
61
+ # Lists
62
+ self.actBullets = QAction("Bulleted list", self)
63
+ self.actBullets.triggered.connect(self.bulletsRequested)
64
+ self.actNumbers = QAction("Numbered list", self)
65
+ self.actNumbers.triggered.connect(self.numbersRequested)
66
+
67
+ # Alignment
68
+ self.actAlignL = QAction("Align left", self)
69
+ self.actAlignC = QAction("Align center", self)
70
+ self.actAlignR = QAction("Align right", self)
71
+ self.actAlignL.triggered.connect(lambda: self.alignRequested.emit(Qt.AlignLeft))
72
+ self.actAlignC.triggered.connect(
73
+ lambda: self.alignRequested.emit(Qt.AlignHCenter)
74
+ )
75
+ self.actAlignR.triggered.connect(
76
+ lambda: self.alignRequested.emit(Qt.AlignRight)
77
+ )
78
+
79
+ self.addActions(
80
+ [
81
+ self.actBold,
82
+ self.actItalic,
83
+ self.actUnderline,
84
+ self.actStrike,
85
+ self.actCode,
86
+ self.actH1,
87
+ self.actH2,
88
+ self.actH3,
89
+ self.actNormal,
90
+ self.actBullets,
91
+ self.actNumbers,
92
+ self.actAlignL,
93
+ self.actAlignC,
94
+ self.actAlignR,
95
+ ]
96
+ )
97
+
98
+ def _apply_toolbar_styles(self):
99
+ self._style_letter_button(self.actBold, "B", bold=True)
100
+ self._style_letter_button(self.actItalic, "I", italic=True)
101
+ self._style_letter_button(self.actUnderline, "U", underline=True)
102
+ self._style_letter_button(self.actStrike, "S", strike=True)
103
+
104
+ # Monospace look for code; use a fixed font
105
+ code_font = QFontDatabase.systemFont(QFontDatabase.FixedFont)
106
+ self._style_letter_button(self.actCode, "</>", custom_font=code_font)
107
+
108
+ # Headings
109
+ self._style_letter_button(self.actH1, "H1")
110
+ self._style_letter_button(self.actH2, "H2")
111
+ self._style_letter_button(self.actH3, "H3")
112
+ self._style_letter_button(self.actNormal, "N")
113
+
114
+ # Lists
115
+ self._style_letter_button(self.actBullets, "•")
116
+ self._style_letter_button(self.actNumbers, "1.")
117
+
118
+ # Alignment
119
+ self._style_letter_button(self.actAlignL, "L")
120
+ self._style_letter_button(self.actAlignC, "C")
121
+ self._style_letter_button(self.actAlignR, "R")
122
+
123
+ def _style_letter_button(
124
+ self,
125
+ action: QAction,
126
+ text: str,
127
+ *,
128
+ bold: bool = False,
129
+ italic: bool = False,
130
+ underline: bool = False,
131
+ strike: bool = False,
132
+ custom_font: QFont | None = None,
133
+ ):
134
+ btn = self.widgetForAction(action)
135
+ if not btn:
136
+ return
137
+ btn.setText(text)
138
+ f = custom_font if custom_font is not None else QFont(btn.font())
139
+ if custom_font is None:
140
+ f.setBold(bold)
141
+ f.setItalic(italic)
142
+ f.setUnderline(underline)
143
+ f.setStrikeOut(strike)
144
+ btn.setFont(f)
145
+
146
+ # Keep accessibility/tooltip readable
147
+ btn.setToolTip(action.text())
148
+ btn.setAccessibleName(action.text())
@@ -1,7 +1,8 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: bouquin
3
- Version: 0.1.1
3
+ Version: 0.1.3
4
4
  Summary: Bouquin is a simple, opinionated notebook application written in Python, PyQt and SQLCipher.
5
+ Home-page: https://git.mig5.net/mig5/bouquin
5
6
  License: GPL-3.0-or-later
6
7
  Author: Miguel Jacq
7
8
  Author-email: mig@mig5.net
@@ -14,6 +15,7 @@ Classifier: Programming Language :: Python :: 3.11
14
15
  Classifier: Programming Language :: Python :: 3.12
15
16
  Requires-Dist: pyside6 (>=6.8.1,<7.0.0)
16
17
  Requires-Dist: sqlcipher3-wheels (>=0.5.5.post0,<0.6.0)
18
+ Project-URL: Repository, https://git.mig5.net/mig5/bouquin
17
19
  Description-Content-Type: text/markdown
18
20
 
19
21
  # Bouquin
@@ -27,7 +29,7 @@ It uses [SQLCipher bindings](https://pypi.org/project/sqlcipher3-wheels) as a dr
27
29
  for SQLite3. This means that the underlying database for the notebook is encrypted at rest.
28
30
 
29
31
  To increase security, the SQLCipher key is requested when the app is opened, and is not written
30
- to disk.
32
+ to disk unless the user configures it to be in the settings.
31
33
 
32
34
  There is deliberately no network connectivity or syncing intended.
33
35
 
@@ -37,23 +39,21 @@ There is deliberately no network connectivity or syncing intended.
37
39
 
38
40
  ## Features
39
41
 
42
+ * Data is encrypted at rest
43
+ * Encryption key is prompted for and never stored, unless user chooses to via Settings
40
44
  * Every 'page' is linked to the calendar day
41
- * Basic markdown
45
+ * Text is HTML with basic styling
46
+ * Search
42
47
  * Automatic periodic saving (or explicitly save)
43
- * Navigating from one day to the next automatically saves
44
- * Basic keyboard shortcuts
45
48
  * Transparent integrity checking of the database when it opens
46
-
47
-
48
- ## Yet to do
49
-
50
- * Search
51
- * Taxonomy/tagging
52
- * Export to other formats (plaintext, json, sql etc)
49
+ * Rekey the database (change the password)
50
+ * Export the database to json, txt, html or csv
53
51
 
54
52
 
55
53
  ## How to install
56
54
 
55
+ Make sure you have `libxcb-cursor0` installed (it may be called something else on non-Debian distributions).
56
+
57
57
  ### From source
58
58
 
59
59
  * Clone this repo or download the tarball from the releases page
@@ -65,7 +65,7 @@ There is deliberately no network connectivity or syncing intended.
65
65
 
66
66
  * Download the whl and run it
67
67
 
68
- ### From PyPi
68
+ ### From PyPi/pip
69
69
 
70
70
  * `pip install bouquin`
71
71
 
@@ -0,0 +1,16 @@
1
+ bouquin/__init__.py,sha256=-bBNFYOq80A2Egtpo5V5zWJtYOxQfRZFQ_feve5lkFU,23
2
+ bouquin/__main__.py,sha256=Vdhw8YA1K3wPMlbJQYL5WqvRzAKVeZ16mZQFO9VRmCo,62
3
+ bouquin/db.py,sha256=s3FDphbi6zxpHEFHnz44saZ9qAV4wU4WEiE6V95PkmI,7877
4
+ bouquin/editor.py,sha256=vPLqysUNinUO6gtJQ8uDxJ_BL-lcaq0IXLStlG63k4E,8042
5
+ bouquin/key_prompt.py,sha256=N5UxgDDnVAaoAIs9AqoydPSRjJ4Likda4-ejlE-lr-Y,1076
6
+ bouquin/main.py,sha256=u7Wm5-9LRZDKkzKkK0W6P4oTtDorrrmtwIJWmQCqsRs,351
7
+ bouquin/main_window.py,sha256=48lq5trwORpGWko6jWLGBk-_7PrtaQfZT1l-jbz67rY,15427
8
+ bouquin/search.py,sha256=NAgH_FLjFB2i9bJXEfH3ClO8dWg7geYyoHtmLFNkrwA,6478
9
+ bouquin/settings.py,sha256=aEsIIlYGwSxCVXXMpo98192QzatIIP6OvQDtcKrYWW4,742
10
+ bouquin/settings_dialog.py,sha256=kWR4OeeHd5uQZ6lfHtuYx3UIh_MCb-nhjHcDyhQhpKM,5747
11
+ bouquin/toolbar.py,sha256=i8uNhcAyYczVKPgSgk6tNJ63XxqlhPjLNpjzfM9NDC0,5401
12
+ bouquin-0.1.3.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
13
+ bouquin-0.1.3.dist-info/METADATA,sha256=y2FvqLWDTEYj1E2LCYAezvRsbycKVV71pVy9AaZv9EY,2468
14
+ bouquin-0.1.3.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
15
+ bouquin-0.1.3.dist-info/entry_points.txt,sha256=d2C5Mc85suj1vWg_mmcfFuEBAYEkdwhZquusme5EWuQ,49
16
+ bouquin-0.1.3.dist-info/RECORD,,
bouquin/highlighter.py DELETED
@@ -1,112 +0,0 @@
1
- from __future__ import annotations
2
-
3
- import re
4
- from PySide6.QtGui import QFont, QTextCharFormat, QSyntaxHighlighter, QColor
5
-
6
-
7
- class MarkdownHighlighter(QSyntaxHighlighter):
8
- ST_NORMAL = 0
9
- ST_CODE = 1
10
-
11
- FENCE = re.compile(r"^```")
12
-
13
- def __init__(self, document):
14
- super().__init__(document)
15
-
16
- base_size = document.defaultFont().pointSizeF() or 12.0
17
-
18
- # Monospace for code
19
- self.mono = QFont("Monospace")
20
- self.mono.setStyleHint(QFont.TypeWriter)
21
-
22
- # Light, high-contrast scheme for code
23
- self.col_bg = QColor("#eef2f6") # light code bg
24
- self.col_fg = QColor("#1f2328") # dark text
25
-
26
- # Formats
27
- self.fmt_h = [QTextCharFormat() for _ in range(6)]
28
- for i, f in enumerate(self.fmt_h, start=1):
29
- f.setFontWeight(QFont.Weight.Bold)
30
- f.setFontPointSize(base_size + (7 - i))
31
- self.fmt_bold = QTextCharFormat()
32
- self.fmt_bold.setFontWeight(QFont.Weight.Bold)
33
- self.fmt_italic = QTextCharFormat()
34
- self.fmt_italic.setFontItalic(True)
35
- self.fmt_quote = QTextCharFormat()
36
- self.fmt_quote.setForeground(QColor("#6a737d"))
37
- self.fmt_link = QTextCharFormat()
38
- self.fmt_link.setFontUnderline(True)
39
- self.fmt_list = QTextCharFormat()
40
- self.fmt_list.setFontWeight(QFont.Weight.DemiBold)
41
- self.fmt_strike = QTextCharFormat()
42
- self.fmt_strike.setFontStrikeOut(True)
43
-
44
- # Uniform code style
45
- self.fmt_code = QTextCharFormat()
46
- self.fmt_code.setFont(self.mono)
47
- self.fmt_code.setFontPointSize(max(6.0, base_size - 1))
48
- self.fmt_code.setBackground(self.col_bg)
49
- self.fmt_code.setForeground(self.col_fg)
50
-
51
- # Simple patterns
52
- self.re_heading = re.compile(r"^(#{1,6}) +.*$")
53
- self.re_bold = re.compile(r"\*\*(.+?)\*\*|__(.+?)__")
54
- self.re_italic = re.compile(r"\*(?!\*)(.+?)\*|_(?!_)(.+?)_")
55
- self.re_strike = re.compile(r"~~(.+?)~~")
56
- self.re_inline_code = re.compile(r"`([^`]+)`")
57
- self.re_link = re.compile(r"\[([^\]]+)\]\(([^)]+)\)")
58
- self.re_list = re.compile(r"^ *(?:[-*+] +|[0-9]+[.)] +)")
59
- self.re_quote = re.compile(r"^> ?.*$")
60
-
61
- def highlightBlock(self, text: str) -> None:
62
- prev = self.previousBlockState()
63
- in_code = prev == self.ST_CODE
64
-
65
- if in_code:
66
- # Entire line is code
67
- self.setFormat(0, len(text), self.fmt_code)
68
- if self.FENCE.match(text):
69
- self.setCurrentBlockState(self.ST_NORMAL)
70
- else:
71
- self.setCurrentBlockState(self.ST_CODE)
72
- return
73
-
74
- # Starting/ending a fenced block?
75
- if self.FENCE.match(text):
76
- self.setFormat(0, len(text), self.fmt_code)
77
- self.setCurrentBlockState(self.ST_CODE)
78
- return
79
-
80
- # --- Normal markdown styling ---
81
- m = self.re_heading.match(text)
82
- if m:
83
- level = min(len(m.group(1)), 6)
84
- self.setFormat(0, len(text), self.fmt_h[level - 1])
85
- self.setCurrentBlockState(self.ST_NORMAL)
86
- return
87
-
88
- m = self.re_list.match(text)
89
- if m:
90
- self.setFormat(m.start(), m.end() - m.start(), self.fmt_list)
91
-
92
- if self.re_quote.match(text):
93
- self.setFormat(0, len(text), self.fmt_quote)
94
-
95
- for m in self.re_inline_code.finditer(text):
96
- self.setFormat(m.start(), m.end() - m.start(), self.fmt_code)
97
-
98
- for m in self.re_bold.finditer(text):
99
- self.setFormat(m.start(), m.end() - m.start(), self.fmt_bold)
100
-
101
- for m in self.re_italic.finditer(text):
102
- self.setFormat(m.start(), m.end() - m.start(), self.fmt_italic)
103
-
104
- for m in self.re_strike.finditer(text):
105
- self.setFormat(m.start(), m.end() - m.start(), self.fmt_strike)
106
-
107
- for m in self.re_link.finditer(text):
108
- start = m.start(1) - 1
109
- length = len(m.group(1)) + 2
110
- self.setFormat(start, length, self.fmt_link)
111
-
112
- self.setCurrentBlockState(self.ST_NORMAL)
@@ -1,14 +0,0 @@
1
- bouquin/__init__.py,sha256=-bBNFYOq80A2Egtpo5V5zWJtYOxQfRZFQ_feve5lkFU,23
2
- bouquin/__main__.py,sha256=Vdhw8YA1K3wPMlbJQYL5WqvRzAKVeZ16mZQFO9VRmCo,62
3
- bouquin/db.py,sha256=-RCWeStZD-eZ2TEUaUTj67cBvq30q2aiSmLoP-K67Jk,3396
4
- bouquin/highlighter.py,sha256=UPP4G4jdN7U8y1c3nk9zTswkHHJw0Tpl3PX6obZrSG0,4077
5
- bouquin/key_prompt.py,sha256=RNrW0bN4xnwDGeBlgbmFaBSs_2iQyYrBYpKOQhe4E0c,1092
6
- bouquin/main.py,sha256=tx59cJZnGgHQ1UHQbdlYgaC36_L0ulyKaOoy6oURXVA,348
7
- bouquin/main_window.py,sha256=OQQ1BhPp3F3ULVrtjeTsKTraZZDzRECjYCORrRpWmis,9291
8
- bouquin/settings.py,sha256=bJYQXbTqX_r_DfOKuGnah6IVZLiNwZAuBuz2OgdhA_E,670
9
- bouquin/settings_dialog.py,sha256=NA40-RvXvM8SND-o5K6b-yKMrShY6NQnQuDAxUoCzyI,3120
10
- bouquin-0.1.1.dist-info/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
11
- bouquin-0.1.1.dist-info/METADATA,sha256=j1CnzeNbB-bzfHaXN9rS66RDaG3-q4Ic6erwzKjqxBw,2148
12
- bouquin-0.1.1.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
13
- bouquin-0.1.1.dist-info/entry_points.txt,sha256=d2C5Mc85suj1vWg_mmcfFuEBAYEkdwhZquusme5EWuQ,49
14
- bouquin-0.1.1.dist-info/RECORD,,