From e690243fc3ce85bde960b2e68d895ee232c17319 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Thu, 19 Jun 2025 20:21:19 +0200
Subject: [PATCH 01/21] Show notes and story structure comments in the viewer
---
novelwriter/gui/docviewer.py | 2 ++
1 file changed, 2 insertions(+)
diff --git a/novelwriter/gui/docviewer.py b/novelwriter/gui/docviewer.py
index 9657ee4e..4b43937b 100644
--- a/novelwriter/gui/docviewer.py
+++ b/novelwriter/gui/docviewer.py
@@ -228,6 +228,8 @@ class GuiDocViewer(QTextBrowser):
qDoc.setTheme(self._docTheme)
qDoc.initDocument()
qDoc.setKeywords(True)
+ qDoc.setCommentType(nwComment.NOTE, CONFIG.viewComments)
+ qDoc.setCommentType(nwComment.STORY, CONFIG.viewComments)
qDoc.setCommentType(nwComment.PLAIN, CONFIG.viewComments)
qDoc.setCommentType(nwComment.SYNOPSIS, CONFIG.viewSynopsis)
qDoc.setCommentType(nwComment.SHORT, CONFIG.viewSynopsis)
From b6de4fe8237ff9ff73a8e8e9667975bd50eac7f5 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Fri, 20 Jun 2025 00:13:15 +0200
Subject: [PATCH 02/21] First iteration of the espresso theme
---
novelwriter/assets/themes/espresso.conf | 75 +++++++++++++++++++++++++
1 file changed, 75 insertions(+)
create mode 100644 novelwriter/assets/themes/espresso.conf
diff --git a/novelwriter/assets/themes/espresso.conf b/novelwriter/assets/themes/espresso.conf
new file mode 100644
index 00000000..5639e51e
--- /dev/null
+++ b/novelwriter/assets/themes/espresso.conf
@@ -0,0 +1,75 @@
+[Main]
+name = Espresso
+mode = dark
+author = Veronica Berglyd Olsen
+credit = Sainnhe Park and Antoine Cotten
+url = https://github.com/sainnhe/sonokai
+
+[Base]
+base = #2f2726
+default = #e5e4e2
+faded = #96817b
+red = #f86882
+orange = #f08d71
+yellow = #f0c66f
+green = #a6cd77
+cyan = #81d0c9
+blue = #89a8e0
+purple = #9fa0e1
+
+[Project]
+root = blue
+folder = yellow
+file = default
+title = green
+chapter = red
+scene = blue
+note = yellow
+active = green
+inactive = red
+disabled = faded
+
+[Palette]
+window = base:L125
+windowtext = default
+base = base
+alternatebase = #51403c
+text = default
+tooltipbase = yellow:L115
+tooltiptext = base
+button = base
+buttontext = default
+brighttext = base
+highlight = #4e5f80
+highlightedtext = default
+link = blue
+linkvisited = blue
+accent = blue
+
+[GUI]
+helptext = blue
+fadedtext = faded
+errortext = red
+
+[Syntax]
+background = base
+text = default
+line = #392e2c
+link = blue
+headertext = yellow:L125
+headertag = faded
+emphasis = orange
+dialog = yellow
+altdialog = orange
+note = yellow:L125
+hidden = faded
+shortcode = green
+keyword = red
+tag = green
+value = orange
+optional = yellow
+spellcheckline = red
+errorline = green
+replacetag = cyan
+modifier = blue
+texthighlight = yellow:96
From e1b082d189315f3e6cd8ee44cb0b5046ca05bbe5 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Fri, 20 Jun 2025 12:03:20 +0200
Subject: [PATCH 03/21] Improve tooltip and highlight
---
novelwriter/assets/themes/espresso.conf | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/novelwriter/assets/themes/espresso.conf b/novelwriter/assets/themes/espresso.conf
index 5639e51e..3d222896 100644
--- a/novelwriter/assets/themes/espresso.conf
+++ b/novelwriter/assets/themes/espresso.conf
@@ -35,7 +35,7 @@ windowtext = default
base = base
alternatebase = #51403c
text = default
-tooltipbase = yellow:L115
+tooltipbase = yellow:L125
tooltiptext = base
button = base
buttontext = default
@@ -72,4 +72,4 @@ spellcheckline = red
errorline = green
replacetag = cyan
modifier = blue
-texthighlight = yellow:96
+texthighlight = yellow:108
From 6ed572772f657fa60375a08f7d711524c130f19a Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Fri, 20 Jun 2025 16:47:11 +0200
Subject: [PATCH 04/21] Add theme description
---
novelwriter/assets/themes/espresso.conf | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/novelwriter/assets/themes/espresso.conf b/novelwriter/assets/themes/espresso.conf
index 3d222896..74ff4b33 100644
--- a/novelwriter/assets/themes/espresso.conf
+++ b/novelwriter/assets/themes/espresso.conf
@@ -1,9 +1,10 @@
[Main]
-name = Espresso
-mode = dark
-author = Veronica Berglyd Olsen
-credit = Sainnhe Park and Antoine Cotten
-url = https://github.com/sainnhe/sonokai
+name = Espresso
+mode = dark
+description = High contrast & vivid color scheme based on Monokai Pro
+author = Veronica Berglyd Olsen
+credit = Sainnhe Park and Antoine Cotten
+url = https://github.com/sainnhe/sonokai
[Base]
base = #2f2726
From aff634a9707938cf0e4d94cdad95fff39d4b8097 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Fri, 20 Jun 2025 16:54:14 +0200
Subject: [PATCH 05/21] Add Noctis Lux theme
---
novelwriter/assets/themes/noctis_lux.conf | 107 ++++++++++++++++++++++
1 file changed, 107 insertions(+)
create mode 100644 novelwriter/assets/themes/noctis_lux.conf
diff --git a/novelwriter/assets/themes/noctis_lux.conf b/novelwriter/assets/themes/noctis_lux.conf
new file mode 100644
index 00000000..65090ef3
--- /dev/null
+++ b/novelwriter/assets/themes/noctis_lux.conf
@@ -0,0 +1,107 @@
+[Main]
+name = Noctis Lux
+mode = light
+description = A light theme with a well balanced blend of warm and cold medium contrast colors
+author = Veronica Berglyd Olsen
+credit = Liviu Schera, and contributors
+url = https://github.com/liviuschera/noctis
+
+##
+# Noctis Lux : themes/lux.json
+##
+# colors
+# selection.background : #169fb155 -> Palette.highlight
+# sideBar.background : #f9f1e1 -> Palette.window
+# editor.background : #fef8ec -> Base.base
+# editor.foreground : #005661 -> Base.default
+# editor.selectionBackground : #ade2eb77 -> Syntax.line, Syntax.texthighlight
+# textLink.foreground : #00c6e0 -> Palette.link, Syntax.link
+# input.background : #fef8ec -> Palette.button
+# notifications.foreground : #005661 -> Palette.tooltiptext
+# notifications.background : #f2edde -> Palette.tooltipbase
+##
+# tokenColors
+# COMMENT : #8ca6a6 -> Base.faded
+# TEXT : #004d57 -> Syntax.text, Syntax.optional
+# KEYWORD : #ff5792 -> Base.red
+# VARIABLE : #fa8900 -> Palette.accent
+# ANNOTATION : #b3694d -> Syntax.note, Syntax.emphasis
+# CONSTANT : #a88c00 -> Base.yellow
+# TAG : #e64100 -> Base.orange
+# STRING : #00b368 -> Syntax.headertext, Palette.accent
+# STRINGINTERPOLATED : #009456 -> Base.green
+# NUMBER : #5842ff -> Base.purple
+# FUNCTION : #0095a8 -> Base.cyan
+# SUPPORT : #00bdd6 -> Syntax.replacetag
+# MISC : #0094f0 -> Base.blue
+# INVALID : #ff530f -> GUI.errortext
+##
+
+[Base]
+base = #fef8ec
+default = #005661
+faded = #8ca6a6
+red = #ff5792
+orange = #e64100
+yellow = #a88c00
+green = #009456
+cyan = #0095a8
+blue = #0094f0
+purple = #5842ff
+
+[Project]
+root = blue
+folder = yellow
+file = default
+title = green
+chapter = red
+scene = blue
+note = yellow
+active = green
+inactive = red
+disabled = faded
+
+[Palette]
+window = #f9f1e1
+windowtext = default
+base = base
+alternatebase = base:D115
+text = default
+tooltipbase = #f2edde
+tooltiptext = #005661
+button = base
+buttontext = #6a7a7c
+brighttext = base
+highlight = #169fb155
+highlightedtext = default
+link = #00c6e0
+linkvisited = blue
+accent = #00b368
+
+[GUI]
+helptext = #fa8900
+fadedtext = faded
+errortext = #ff530f
+
+[Syntax]
+background = base
+text = #004d57
+line = #ade2eb56
+link = #00c6e0
+headertext = #00b368
+headertag = green:D125
+emphasis = #b3694d
+dialog = green
+altdialog = blue
+note = #b3694d
+hidden = faded
+shortcode = red
+keyword = yellow
+tag = blue
+value = red:D125
+optional = #004d57
+spellcheckline = red
+errorline = green
+replacetag = #00bdd6
+modifier = red
+texthighlight = #ade2eb77
From ab1d55611f86101086cea199b8518d864ba21f3b Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Fri, 20 Jun 2025 17:06:50 +0200
Subject: [PATCH 06/21] Add Noctis theme
---
novelwriter/assets/themes/noctis.conf | 107 ++++++++++++++++++++++++++
1 file changed, 107 insertions(+)
create mode 100644 novelwriter/assets/themes/noctis.conf
diff --git a/novelwriter/assets/themes/noctis.conf b/novelwriter/assets/themes/noctis.conf
new file mode 100644
index 00000000..0b5cfe7f
--- /dev/null
+++ b/novelwriter/assets/themes/noctis.conf
@@ -0,0 +1,107 @@
+[Main]
+name = Noctis
+mode = dark
+description = A dark theme with a well balanced blend of warm and cold medium contrast colors
+author = Veronica Berglyd Olsen
+credit = Liviu Schera, and contributors
+url = https://github.com/liviuschera/noctis
+
+##
+# Noctis Lux : themes/lux.json
+##
+# colors
+# selection.background : #107684 -> Palette.highlight
+# sideBar.background : #041d20 -> Palette.window
+# editor.background : #052529 -> Base.base
+# editor.foreground : #b2cacd -> Base.default
+# editor.selectionBackground : #169fb144 -> Syntax.line, Syntax.texthighlight
+# textLink.foreground : #40d4e7 -> Palette.link, Syntax.link
+# input.background : #052529 -> Palette.button
+# notifications.foreground : #cdd3de -> Palette.tooltiptext
+# notifications.background : #073940 -> Palette.tooltipbase
+##
+# tokenColors
+# COMMENT : #5b858b -> Base.faded
+# TEXT : #b2cacd -> Syntax.text, Syntax.optional
+# KEYWORD : #df769b -> Base.red
+# VARIABLE : #e4b781 -> Palette.accent
+# ANNOTATION : #d67e5c -> Syntax.note, Syntax.emphasis
+# CONSTANT : #d5971a -> Base.yellow
+# TAG : #e66533 -> Base.orange
+# STRING : #49e9a6 -> Syntax.headertext, Palette.accent
+# STRINGINTERPOLATED : #16b673 -> Base.green
+# NUMBER : #7060eb -> Base.purple
+# FUNCTION : #16a3b6 -> Base.cyan
+# SUPPORT : #49d6e9 -> Syntax.replacetag
+# MISC : #49ace9 -> Base.blue
+# INVALID : #e3541c -> GUI.errortext
+##
+
+[Base]
+base = #052529
+default = #b2cacd
+faded = #5b858b
+red = #df769b
+orange = #e66533
+yellow = #d5971a
+green = #16b673
+cyan = #16a3b6
+blue = #49ace9
+purple = #7060eb
+
+[Project]
+root = blue
+folder = yellow
+file = default
+title = green
+chapter = red
+scene = blue
+note = yellow
+active = green
+inactive = red
+disabled = faded
+
+[Palette]
+window = #041d20
+windowtext = default
+base = base
+alternatebase = base:L175
+text = default
+tooltipbase = #073940
+tooltiptext = #cdd3de
+button = base
+buttontext = #6a7a7c
+brighttext = base
+highlight = #107684
+highlightedtext = default
+link = #40d4e7
+linkvisited = blue
+accent = #49e9a6
+
+[GUI]
+helptext = #e4b781
+fadedtext = faded
+errortext = #e3541c
+
+[Syntax]
+background = base
+text = #b2cacd
+line = #169fb132
+link = #40d4e7
+headertext = #49e9a6
+headertag = green:D125
+emphasis = #d67e5c
+dialog = green
+altdialog = blue
+note = #d67e5c
+hidden = faded
+shortcode = red
+keyword = yellow
+tag = blue
+value = red:D125
+optional = #b2cacd
+spellcheckline = red
+errorline = green
+replacetag = #49d6e9
+modifier = red
+texthighlight = #169fb144
From cf3f6caca85e77faeadc9da97f806856389ed231 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Fri, 20 Jun 2025 17:12:00 +0200
Subject: [PATCH 07/21] Fix Noctis button text colour
---
novelwriter/assets/themes/noctis.conf | 5 +++--
novelwriter/assets/themes/noctis_lux.conf | 1 +
2 files changed, 4 insertions(+), 2 deletions(-)
diff --git a/novelwriter/assets/themes/noctis.conf b/novelwriter/assets/themes/noctis.conf
index 0b5cfe7f..35820118 100644
--- a/novelwriter/assets/themes/noctis.conf
+++ b/novelwriter/assets/themes/noctis.conf
@@ -7,7 +7,7 @@ credit = Liviu Schera, and contributors
url = https://github.com/liviuschera/noctis
##
-# Noctis Lux : themes/lux.json
+# Noctis Lux : themes/noctis.json
##
# colors
# selection.background : #107684 -> Palette.highlight
@@ -17,6 +17,7 @@ url = https://github.com/liviuschera/noctis
# editor.selectionBackground : #169fb144 -> Syntax.line, Syntax.texthighlight
# textLink.foreground : #40d4e7 -> Palette.link, Syntax.link
# input.background : #052529 -> Palette.button
+# input.foreground : #cdd3de -> Palette.buttontext
# notifications.foreground : #cdd3de -> Palette.tooltiptext
# notifications.background : #073940 -> Palette.tooltipbase
##
@@ -70,7 +71,7 @@ text = default
tooltipbase = #073940
tooltiptext = #cdd3de
button = base
-buttontext = #6a7a7c
+buttontext = #cdd3de
brighttext = base
highlight = #107684
highlightedtext = default
diff --git a/novelwriter/assets/themes/noctis_lux.conf b/novelwriter/assets/themes/noctis_lux.conf
index 65090ef3..ddf34244 100644
--- a/novelwriter/assets/themes/noctis_lux.conf
+++ b/novelwriter/assets/themes/noctis_lux.conf
@@ -17,6 +17,7 @@ url = https://github.com/liviuschera/noctis
# editor.selectionBackground : #ade2eb77 -> Syntax.line, Syntax.texthighlight
# textLink.foreground : #00c6e0 -> Palette.link, Syntax.link
# input.background : #fef8ec -> Palette.button
+# input.foreground : #6a7a7c -> Palette.buttontext
# notifications.foreground : #005661 -> Palette.tooltiptext
# notifications.background : #f2edde -> Palette.tooltipbase
##
From 3c474987b29ba94ae729c8e4e24e8d58a95feafd Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Mon, 23 Jun 2025 23:24:20 +0200
Subject: [PATCH 08/21] Check indent and justify after lines are combined in
tokenizer, not before (#2426)
---
novelwriter/formats/tokenizer.py | 34 ++++++-----
tests/test_formats/test_fmt_tokenizer.py | 74 ++++++++++++++++++++++++
tests/test_formats/test_fmt_toodt.py | 2 +-
3 files changed, 93 insertions(+), 17 deletions(-)
diff --git a/novelwriter/formats/tokenizer.py b/novelwriter/formats/tokenizer.py
index e6d2d563..e0587002 100644
--- a/novelwriter/formats/tokenizer.py
+++ b/novelwriter/formats/tokenizer.py
@@ -880,31 +880,21 @@ class Tokenizer(ABC):
if nBlock[0] != BlockTyp.TEXT:
# Next block is not text, so we add the buffer to blocks
nLines = len(pLines)
- cStyle = pLines[0][4]
- if firstIndent and not (self._noIndent or cStyle & BlockFmt.ALIGNED):
- # If paragraph indentation is enabled, not temporarily
- # turned off, and the block is not aligned, we add the
- # text indentation flag
- cStyle |= BlockFmt.IND_T
+ tFmt: T_Formats = []
+ pTxt = ""
+ cStyle = BlockFmt.NONE
if nLines == 1:
- # The paragraph contains a single line, so we just save
- # that directly to the blocks list. If justify is
- # enabled, and there is no alignment, we apply it.
- if doJustify and not cStyle & BlockFmt.ALIGNED:
- cStyle |= BlockFmt.JUSTIFY
-
+ # The paragraph contains a single line
+ tFmt = pLines[0][3]
pTxt = pLines[0][2].translate(transMapB)
- sBlocks.append((
- BlockTyp.TEXT, pLines[0][1], pTxt, pLines[0][3], cStyle
- ))
+ cStyle = pLines[0][4]
elif nLines > 1:
# The paragraph contains multiple lines, so we need to
# join them according to the line break policy, and
# recompute all the formatting markers
tTxt = ""
- tFmt: T_Formats = []
for aBlock in pLines:
tLen = len(tTxt)
tTxt += f"{aBlock[2]}{lineSep}"
@@ -912,6 +902,18 @@ class Tokenizer(ABC):
cStyle |= aBlock[4]
pTxt = tTxt[:-1].translate(transMapB)
+
+ if nLines:
+ isAligned = cStyle & BlockFmt.ALIGNED
+ if firstIndent and not (self._noIndent or isAligned):
+ # If paragraph indentation is enabled, not temporarily
+ # turned off, and the block is not aligned, we add the
+ # text indentation flag
+ cStyle |= BlockFmt.IND_T
+
+ if doJustify and not isAligned:
+ cStyle |= BlockFmt.JUSTIFY
+
sBlocks.append((
BlockTyp.TEXT, pLines[0][1], pTxt, tFmt, cStyle
))
diff --git a/tests/test_formats/test_fmt_tokenizer.py b/tests/test_formats/test_fmt_tokenizer.py
index 7795601c..58106cdd 100644
--- a/tests/test_formats/test_fmt_tokenizer.py
+++ b/tests/test_formats/test_fmt_tokenizer.py
@@ -1074,6 +1074,80 @@ def testFmtToken_Paragraphs(mockGUI):
]
+@pytest.mark.core
+def testFmtToken_BreakAlignIndent(mockGUI):
+ """Test the splitting of paragraphs with alignment."""
+ project = NWProject()
+ tokens = BareTokenizer(project)
+ tokens._handle = TMH
+
+ for text in [
+ "This is text <<\nspanning multiple\nlines",
+ "This is text\nspanning multiple <<\nlines",
+ "This is text\nspanning multiple\nlines <<",
+ ]:
+ # Preserve Breaks
+ tokens.setKeepLineBreaks(True)
+ tokens._text = text
+ tokens.tokenizeText()
+ assert tokens._blocks == [
+ (BlockTyp.TEXT, "", "This is text\nspanning multiple\nlines", [], BlockFmt.LEFT),
+ ]
+
+ # Don't Preserve Breaks
+ tokens.setKeepLineBreaks(False)
+ tokens._text = text
+ tokens.tokenizeText()
+ assert tokens._blocks == [
+ (BlockTyp.TEXT, "", "This is text spanning multiple lines", [], BlockFmt.LEFT),
+ ]
+
+ # With Justify
+ # This should disable justify
+ tokens.setKeepLineBreaks(True)
+ tokens.setJustify(True)
+ tokens._text = text
+ tokens.tokenizeText()
+ assert tokens._blocks == [
+ (BlockTyp.TEXT, "", "This is text\nspanning multiple\nlines", [], BlockFmt.LEFT),
+ ]
+
+ # With Indent
+ # This should disable indent
+ tokens.setKeepLineBreaks(True)
+ tokens.setFirstLineIndent(True, 1.0, False)
+ tokens._text = text
+ tokens.tokenizeText()
+ assert tokens._blocks == [
+ (BlockTyp.TEXT, "", "This is text\nspanning multiple\nlines", [], BlockFmt.LEFT),
+ ]
+
+
+@pytest.mark.core
+def testFmtToken_BreakJustify(mockGUI):
+ """Test the of processing of justify with breaks."""
+ project = NWProject()
+ tokens = BareTokenizer(project)
+ tokens._handle = TMH
+ tokens.setJustify(True)
+
+ # Applied to all lines when breaks are preserved
+ tokens._text = "This is text\nspanning multiple\nlines"
+ tokens.setKeepLineBreaks(True)
+ tokens.tokenizeText()
+ assert tokens._blocks == [
+ (BlockTyp.TEXT, "", "This is text\nspanning multiple\nlines", [], BlockFmt.JUSTIFY),
+ ]
+
+ # Turning off breaks should make no difference (see issue #2426)
+ tokens._text = "This is text\nspanning multiple\nlines"
+ tokens.setKeepLineBreaks(False)
+ tokens.tokenizeText()
+ assert tokens._blocks == [
+ (BlockTyp.TEXT, "", "This is text spanning multiple lines", [], BlockFmt.JUSTIFY),
+ ]
+
+
@pytest.mark.core
def testFmtToken_TextFormat(mockGUI):
"""Test the tokenization of text formats in the Tokenizer class."""
diff --git a/tests/test_formats/test_fmt_toodt.py b/tests/test_formats/test_fmt_toodt.py
index 70b72874..eded1eeb 100644
--- a/tests/test_formats/test_fmt_toodt.py
+++ b/tests/test_formats/test_fmt_toodt.py
@@ -733,7 +733,7 @@ def testFmtToOdt_ConvertParagraphs(mockGUI):
''
'Scene'
'Regular paragraph'
- 'withbreak'
+ 'withbreak'
'Left Align'
''
)
From 979a55d24a1e3261cb04cc92e06ee523baa24927 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Tue, 24 Jun 2025 00:07:55 +0200
Subject: [PATCH 09/21] Add alignment precedence test to HTML tests
---
tests/test_formats/test_fmt_tohtml.py | 51 +++++++++++++++++++++++++--
1 file changed, 48 insertions(+), 3 deletions(-)
diff --git a/tests/test_formats/test_fmt_tohtml.py b/tests/test_formats/test_fmt_tohtml.py
index fb05ebfd..28b8e3e5 100644
--- a/tests/test_formats/test_fmt_tohtml.py
+++ b/tests/test_formats/test_fmt_tohtml.py
@@ -147,9 +147,6 @@ def testFmtToHtml_ConvertParagraphs(mockGUI):
html._isNovel = True
html._isFirst = True
- # Paragraphs
- # ==========
-
# Text
html._text = "Some **nested bold and _italic_ and ~~strikethrough~~ text** here\n"
html.tokenizeText()
@@ -280,6 +277,54 @@ def testFmtToHtml_ConvertParagraphs(mockGUI):
)
+@pytest.mark.core
+def testFmtToHtml_Alignment(mockGUI):
+ """Test paragraph alignment in the ToHtml class."""
+ project = NWProject()
+ html = ToHtml(project)
+ html.initDocument()
+
+ # Left
+ html._text = "This is text <<\nspanning multiple\nlines"
+ html.tokenizeText()
+ html.doConvert()
+ assert html._pages[-1] == (
+ "
This is text
spanning multiple
lines
\n"
+ )
+
+ # Right
+ html._text = ">> This is text\nspanning multiple\nlines"
+ html.tokenizeText()
+ html.doConvert()
+ assert html._pages[-1] == (
+ "This is text
spanning multiple
lines
\n"
+ )
+
+ # Centre
+ html._text = ">> This is text <<\nspanning multiple\nlines"
+ html.tokenizeText()
+ html.doConvert()
+ assert html._pages[-1] == (
+ "This is text
spanning multiple
lines
\n"
+ )
+
+ # Left before Right
+ html._text = ">> This is text\nspanning multiple <<\nlines"
+ html.tokenizeText()
+ html.doConvert()
+ assert html._pages[-1] == (
+ "This is text
spanning multiple
lines
\n"
+ )
+
+ # Right before Centre
+ html._text = ">> This is text <<\n>> spanning multiple\nlines"
+ html.tokenizeText()
+ html.doConvert()
+ assert html._pages[-1] == (
+ "This is text
spanning multiple
lines
\n"
+ )
+
+
@pytest.mark.core
def testFmtToHtml_Dialog(mockGUI):
"""Test paragraph formats in the ToHtml class."""
From a210a665fbd10863e615363c05697281ebd785b8 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Tue, 24 Jun 2025 00:08:08 +0200
Subject: [PATCH 10/21] Update documentation
---
docs/source/usage/advanced_formatting.rst | 14 ++++++
docs/source/usage/alignment_and_indent.rst | 55 ++++++++++++++++++++--
2 files changed, 65 insertions(+), 4 deletions(-)
diff --git a/docs/source/usage/advanced_formatting.rst b/docs/source/usage/advanced_formatting.rst
index 557db1a7..94bfec0f 100644
--- a/docs/source/usage/advanced_formatting.rst
+++ b/docs/source/usage/advanced_formatting.rst
@@ -56,6 +56,20 @@ activated by clicking the left-most icon button in the editor header.
.. versionadded:: 2.2
+.. _docs_usage_formatting_shortcodes_break:
+
+Forced Line Break
+-----------------
+
+Inserting ``[br]`` in the text will ensure a line break is always inserted in that place, even if
+you turn off **Preserve Hard Line breaks** in your **Manuscript Build Settings**.
+
+You can add a manual line break after it too, for a better visual representation in the editor, but
+keep in mind that this line break is removed before the text is processed, so the text on either
+side will be considered as belonging to the same line. This can affect how alignment is treated.
+See :ref:`docs_usage_align_indent_forced` for more details.
+
+
.. _docs_usage_formatting_breaks:
Vertical Space and Page Breaks
diff --git a/docs/source/usage/alignment_and_indent.rst b/docs/source/usage/alignment_and_indent.rst
index 2494ea32..dea1b171 100644
--- a/docs/source/usage/alignment_and_indent.rst
+++ b/docs/source/usage/alignment_and_indent.rst
@@ -53,18 +53,33 @@ the entire paragraph. For the following text, all lines will be centred:
.. code-block:: md
- >> I am the very model of a modern Major-General
+ >> I am the very model of a modern Major-General <<
I've information vegetable, animal, and mineral
I know the kings of England, and I quote the fights historical
- From Marathon to Waterloo, in order categorical <<
+ From Marathon to Waterloo, in order categorical
+
+If you have multiple conflicting alignments on a paragraph, only one is applied. The order of
+precedence is:
+
+#. Left alignment
+#. Right alignment
+#. Centred text
+#. Justified text
+
+.. note::
+
+ It is strongly recommended that you keep the **Preserve Hard Line Breaks** setting enabled in
+ your **Manuscript Build Settings**. This setting assumes all single line breaks in your text are
+ intended. Turning this off makes adding line breaks much more complicated, but it is still
+ possible. See :ref:`docs_usage_align_indent_forced`.
Alignment with First Line Indent
================================
If you have first line indent enabled in your manuscript build settings, you probably want to
-disable it for text in verses. Adding any alignment tags will cause the first line indent to be
-switched off for that paragraph.
+disable it for text in verses. Adding any alignment tags on a paragraph will cause the first
+line indent to be switched off for that paragraph.
:bdg-info:`Example`
@@ -76,3 +91,35 @@ The following text will always be aligned against the left margin:
I've information vegetable, animal, and mineral
I know the kings of England, and I quote the fights historical
From Marathon to Waterloo, in order categorical
+
+
+.. _docs_usage_align_indent_forced:
+
+Alignment with Forced Line Breaks
+=================================
+
+If you turn off **Preserve Hard Line Breaks** in **Manuscript Build Settings**, you can still force
+line breaks in paragraphs using the ``[br]`` shortcode. For clarity in the text, you can add a line
+break after it as well. It doesn't result in two line breaks.
+
+Keep in mind that when the text is processed, these lines on either side of a ``[br]`` shortcode
+are combined, and a trailing hard line break is *ignored*. This means that when such a paragraph is
+processed, these line breaks count as the same line. This affects hiw alignment tags are handled.
+For instance, this text becomes centred instead of left aligned.
+
+.. code-block:: md
+
+ >> I am the very model of a modern Major-General[br]
+ I've information vegetable, animal, and mineral[br]
+ I know the kings of England, and I quote the fights historical[br]
+ From Marathon to Waterloo, in order categorical <<
+
+Since this is understood as one line, this is the only way you can actually centre this paragraph.
+
+.. caution::
+
+ Due to this difference in how text with ``[br]`` tags are processed, it is generally better to
+ stick with the **Preserve Hard Line Breaks** setting enabled. It ensures a better correspondence
+ between what you see in the editor and what output you get.
+
+See also :ref:`docs_usage_formatting_shortcodes_break`.
From c1dd2ebb92ab52f061aa7afd235d2d05c84e587a Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Tue, 24 Jun 2025 00:22:35 +0200
Subject: [PATCH 11/21] Fix typos and inconsistencies
---
docs/source/usage/advanced_formatting.rst | 6 +++---
docs/source/usage/alignment_and_indent.rst | 18 +++++++++---------
2 files changed, 12 insertions(+), 12 deletions(-)
diff --git a/docs/source/usage/advanced_formatting.rst b/docs/source/usage/advanced_formatting.rst
index 94bfec0f..556d6f1f 100644
--- a/docs/source/usage/advanced_formatting.rst
+++ b/docs/source/usage/advanced_formatting.rst
@@ -62,12 +62,12 @@ Forced Line Break
-----------------
Inserting ``[br]`` in the text will ensure a line break is always inserted in that place, even if
-you turn off **Preserve Hard Line breaks** in your **Manuscript Build Settings**.
+you turn off **Preserve Hard Line Breaks** in your manuscript build settings.
You can add a manual line break after it too, for a better visual representation in the editor, but
keep in mind that this line break is removed before the text is processed, so the text on either
-side will be considered as belonging to the same line. This can affect how alignment is treated.
-See :ref:`docs_usage_align_indent_forced` for more details.
+side of the ``[br]`` shortcode will be considered as belonging to the same line. This can affect
+how alignment is treated. See :ref:`docs_usage_align_indent_forced` for more details.
.. _docs_usage_formatting_breaks:
diff --git a/docs/source/usage/alignment_and_indent.rst b/docs/source/usage/alignment_and_indent.rst
index dea1b171..60949b9a 100644
--- a/docs/source/usage/alignment_and_indent.rst
+++ b/docs/source/usage/alignment_and_indent.rst
@@ -69,9 +69,9 @@ precedence is:
.. note::
It is strongly recommended that you keep the **Preserve Hard Line Breaks** setting enabled in
- your **Manuscript Build Settings**. This setting assumes all single line breaks in your text are
- intended. Turning this off makes adding line breaks much more complicated, but it is still
- possible. See :ref:`docs_usage_align_indent_forced`.
+ your manuscript build settings. This setting assumes all single line breaks in your text are
+ intended. Turning this off makes adding line breaks more complicated, but it is still possible.
+ See :ref:`docs_usage_align_indent_forced`.
Alignment with First Line Indent
@@ -98,13 +98,13 @@ The following text will always be aligned against the left margin:
Alignment with Forced Line Breaks
=================================
-If you turn off **Preserve Hard Line Breaks** in **Manuscript Build Settings**, you can still force
-line breaks in paragraphs using the ``[br]`` shortcode. For clarity in the text, you can add a line
-break after it as well. It doesn't result in two line breaks.
+If you turn off **Preserve Hard Line Breaks** in your manuscript build settings, you can still
+force line breaks in paragraphs using the ``[br]`` shortcode. For clarity in the text, you can add
+a line break after it as well. It doesn't result in two line breaks.
-Keep in mind that when the text is processed, these lines on either side of a ``[br]`` shortcode
-are combined, and a trailing hard line break is *ignored*. This means that when such a paragraph is
-processed, these line breaks count as the same line. This affects hiw alignment tags are handled.
+Keep in mind that when the text is processed, the lines on either side of a ``[br]`` shortcode are
+combined, and any trailing hard line break is *ignored*. This means that when such a paragraph is
+processed, these line breaks count as the same line. This affects how alignment tags are handled.
For instance, this text becomes centred instead of left aligned.
.. code-block:: md
From ad2288e2fce5292a042dbc04d851401f2c5adebb Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Tue, 24 Jun 2025 16:05:59 +0200
Subject: [PATCH 12/21] Add tag value for novel docs in Outline and Novel View
(#2428)
---
novelwriter/core/index.py | 14 ++++++++------
novelwriter/core/indexdata.py | 4 +++-
novelwriter/gui/noveltree.py | 1 +
3 files changed, 12 insertions(+), 7 deletions(-)
diff --git a/novelwriter/core/index.py b/novelwriter/core/index.py
index d8e9c8e4..07933690 100644
--- a/novelwriter/core/index.py
+++ b/novelwriter/core/index.py
@@ -708,17 +708,19 @@ class Index:
return 0, 0, 0
def getReferences(self, tHandle: str, sTitle: str | None = None) -> dict[str, list[str]]:
- """Extract all references made in a file, and optionally title
- section.
+ """Extract all tags and references made in a file, and
+ optionally title section.
"""
- tRefs = {x: [] for x in nwKeyWords.VALID_KEYS}
+ refs = {x: [] for x in nwKeyWords.VALID_KEYS}
for rTitle, hItem in self._itemIndex.iterItemHeaders(tHandle):
if sTitle is None or sTitle == rTitle:
for aTag, refTypes in hItem.references.items():
for refType in refTypes:
- if refType in tRefs:
- tRefs[refType].append(self._tagsIndex.tagName(aTag))
- return tRefs
+ if refType in refs:
+ refs[refType].append(self._tagsIndex.tagName(aTag))
+ if tag := hItem.tag:
+ refs[nwKeyWords.TAG_KEY] = [self._tagsIndex.tagName(tag)]
+ return refs
def getReferenceForHeader(self, tHandle: str, nHead: int, keyClass: str) -> list[str]:
"""Get the display names for a tags class for insertion into a
diff --git a/novelwriter/core/indexdata.py b/novelwriter/core/indexdata.py
index 070558ce..4ddad943 100644
--- a/novelwriter/core/indexdata.py
+++ b/novelwriter/core/indexdata.py
@@ -342,12 +342,14 @@ class IndexHeading:
##
def getReferences(self) -> dict[str, list[str]]:
- """Extract all references for this heading."""
+ """Extract all tags and references for this heading."""
refs = {x: [] for x in nwKeyWords.VALID_KEYS}
for tag, types in self._refs.items():
for keyword in types:
if keyword in refs and (name := self._cache.tags.tagName(tag)):
refs[keyword].append(name)
+ if tag := self._tag:
+ refs[nwKeyWords.TAG_KEY] = [self._cache.tags.tagName(tag)]
return refs
def getReferencesByKeyword(self, keyword: str) -> list[str]:
diff --git a/novelwriter/gui/noveltree.py b/novelwriter/gui/noveltree.py
index 752c6d39..86913b61 100644
--- a/novelwriter/gui/noveltree.py
+++ b/novelwriter/gui/noveltree.py
@@ -593,6 +593,7 @@ class GuiNovelTree(NTreeView):
lines = []
if head := SHARED.project.index.getItemHeading(tHandle, sTitle):
tags = head.getReferences()
+ appendTags(tags, nwKeyWords.TAG_KEY, lines)
appendTags(tags, nwKeyWords.POV_KEY, lines)
appendTags(tags, nwKeyWords.FOCUS_KEY, lines)
appendTags(tags, nwKeyWords.CHAR_KEY, lines)
From 5ae385ec02e1a35864ddd43829a7d89a23097f28 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Tue, 24 Jun 2025 16:15:35 +0200
Subject: [PATCH 13/21] Add tag to reference by keyword lookup
---
novelwriter/core/indexdata.py | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/novelwriter/core/indexdata.py b/novelwriter/core/indexdata.py
index 4ddad943..b74ce4c4 100644
--- a/novelwriter/core/indexdata.py
+++ b/novelwriter/core/indexdata.py
@@ -348,16 +348,20 @@ class IndexHeading:
for keyword in types:
if keyword in refs and (name := self._cache.tags.tagName(tag)):
refs[keyword].append(name)
- if tag := self._tag:
- refs[nwKeyWords.TAG_KEY] = [self._cache.tags.tagName(tag)]
+ if name := self._cache.tags.tagName(self._tag):
+ refs[nwKeyWords.TAG_KEY] = [name]
return refs
def getReferencesByKeyword(self, keyword: str) -> list[str]:
"""Extract all references for this heading."""
refs = []
- for tag, types in self._refs.items():
- if keyword in types and (name := self._cache.tags.tagName(tag)):
+ if keyword == nwKeyWords.TAG_KEY:
+ if name := self._cache.tags.tagName(self._tag):
refs.append(name)
+ else:
+ for tag, types in self._refs.items():
+ if keyword in types and (name := self._cache.tags.tagName(tag)):
+ refs.append(name)
return refs
##
From aaf4ede04d78a39e40c413e2e3e7dccb440c018e Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Tue, 24 Jun 2025 16:15:49 +0200
Subject: [PATCH 14/21] Update test coverage
---
tests/test_core/test_core_index.py | 5 ++++-
tests/test_core/test_core_indexdata.py | 6 ++++--
2 files changed, 8 insertions(+), 3 deletions(-)
diff --git a/tests/test_core/test_core_index.py b/tests/test_core/test_core_index.py
index dca4575a..e86b8dc0 100644
--- a/tests/test_core/test_core_index.py
+++ b/tests/test_core/test_core_index.py
@@ -695,6 +695,7 @@ def testCoreIndex_ExtractData(nwGUI, fncPath, mockRnd):
))
assert index.scanText(nHandle, (
"# Hello World!\n"
+ "@tag: Scene\n"
"@pov: Jane\n"
"@char: Jane, John\n\n"
"% this is a comment\n\n"
@@ -749,11 +750,13 @@ def testCoreIndex_ExtractData(nwGUI, fncPath, mockRnd):
# Look up an invalid handle
refs = index.getReferences("Not a handle")
+ assert refs["@tag"] == []
assert refs["@pov"] == []
assert refs["@char"] == []
# The novel file should now refer to Jane as @pov and @char
refs = index.getReferences(nHandle)
+ assert refs["@tag"] == ["Scene"]
assert refs["@pov"] == ["Jane"]
assert refs["@char"] == ["Jane", "John"]
@@ -791,7 +794,7 @@ def testCoreIndex_ExtractData(nwGUI, fncPath, mockRnd):
# getKeyWordTags
# ==============
- assert index.getKeyWordTags("@mention") == ["Jane", "John"]
+ assert index.getKeyWordTags("@mention") == ["Jane", "John", "Scene"]
assert index.getKeyWordTags("@char") == ["Jane", "John"]
assert index.getKeyWordTags("@plot") == []
assert index.getKeyWordTags("@tag") == []
diff --git a/tests/test_core/test_core_indexdata.py b/tests/test_core/test_core_indexdata.py
index 5bf45c8a..d4797aee 100644
--- a/tests/test_core/test_core_indexdata.py
+++ b/tests/test_core/test_core_indexdata.py
@@ -267,6 +267,7 @@ def testCoreIndexData_IndexHeadingReferences():
head = IndexHeading(cache, "T0001")
# Add some references
+ head.setTag("Scene")
head.addReference("Jane", "@pov")
head.addReference("Jane", "@char")
head.addReference("John", "@char")
@@ -290,6 +291,7 @@ def testCoreIndexData_IndexHeadingReferences():
}
# Set names
+ cache.tags.add("Scene", "Scene", "0000000000000", "T00001", "NOVEL")
cache.tags.add("Jane", "Jane", "0000000000000", "T00001", "CHARACTER")
cache.tags.add("John", "John", "0000000000000", "T00001", "CHARACTER")
cache.tags.add("Main", "Main", "0000000000000", "T00001", "PLOT")
@@ -301,7 +303,7 @@ def testCoreIndexData_IndexHeadingReferences():
"@plot": ["Main"],
"@object": ["Gun"],
"@story": [],
- "@tag": [],
+ "@tag": ["Scene"],
"@focus": [],
"@custom": [],
"@time": [],
@@ -316,7 +318,7 @@ def testCoreIndexData_IndexHeadingReferences():
assert head.getReferencesByKeyword("@plot") == ["Main"]
assert head.getReferencesByKeyword("@object") == ["Gun"]
assert head.getReferencesByKeyword("@story") == []
- assert head.getReferencesByKeyword("@tag") == []
+ assert head.getReferencesByKeyword("@tag") == ["Scene"]
assert head.getReferencesByKeyword("@focus") == []
assert head.getReferencesByKeyword("@custom") == []
assert head.getReferencesByKeyword("@time") == []
From b4db960e685aa0435987d3149146e66dd95e5c0f Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Tue, 24 Jun 2025 16:16:15 +0200
Subject: [PATCH 15/21] Add scene tag to sample main scene
---
sample/content/636b6aa9b697b.nwd | 5 +++--
sample/nwProject.nwx | 6 +++---
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/sample/content/636b6aa9b697b.nwd b/sample/content/636b6aa9b697b.nwd
index e3d4bfde..416efc4e 100644
--- a/sample/content/636b6aa9b697b.nwd
+++ b/sample/content/636b6aa9b697b.nwd
@@ -1,10 +1,11 @@
%%~name: Making a Scene
%%~path: 6a2d6d5f4f401/636b6aa9b697b
%%~kind: NOVEL/DOCUMENT
-%%~hash: 1f3d98a6a27b4f9a7f2239fcd78f9e2263cd3b97
-%%~date: Unknown/2025-05-18 22:36:59
+%%~hash: b39201b02e3db63493d61d09e5fec5765a937255
+%%~date: Unknown/2025-06-24 09:09:56
### Making a Scene
+@tag: Scene
@pov: Jane
@char: John, Jane
@location: Earth
diff --git a/sample/nwProject.nwx b/sample/nwProject.nwx
index ae0fee4d..c53ff1c9 100644
--- a/sample/nwProject.nwx
+++ b/sample/nwProject.nwx
@@ -1,6 +1,6 @@
-
-
+
+
Sample Project
Jane Smith
@@ -58,7 +58,7 @@
Chapter One
-
-
+
Making a Scene
-
From b16783143f1a2581a635aaa7562bac2bef975e6e Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Tue, 24 Jun 2025 16:37:50 +0200
Subject: [PATCH 16/21] Bump version to 2.7.2 and update changelog
---
CHANGELOG.md | 40 ++++++++++++++++++++++++++++++++++++++++
novelwriter/__init__.py | 6 +++---
sample/nwProject.nwx | 4 ++--
3 files changed, 45 insertions(+), 5 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 455d9449..209aba53 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,45 @@
# novelWriter Changelog
+## Version 2.7.2 [2025-06-24]
+
+### Release Notes
+
+This is a patch release that fixes several issues related to DocX and PDF manuscript documents,
+improves how line breaks, alignment and indentation is processed, and fixes some issues with
+displaying tags for novel documents on the user interface.
+
+### Detailed Changelog
+
+**Bugfixes**
+
+* Fixed an issue where escaped markup characters were not replaced properly in DocX and PDF
+ documents, and in previews. Issue #2410. PR #2411.
+* Fixed an issue where titles with line breaks in them would have page breaks applied to both lines
+ for preview and PDF documents. Issue #2415. PR #2416.
+* When comments are enabled in the viewer, story comments should also be visible. A separate filter
+ button will be added for this in 2.8. PR #2420.
+* Fixed an issue where the justified text setting would not be properly applied to a paragraph in a
+ manuscript document if there was a line break in the paragraph, but single line breaks were set
+ to be ignored. Issue #2426. PR #2427.
+* Fixed an issue where the "Tag" field of the Outline View details panel remained blank even if a
+ tag was set for the novel document. Issue #2428. PR #2429.
+
+**Improvements**
+
+* When a paragraph has line breaks in it, the alignment tag will now override first line
+ indentation even if the alignment tag is not on the first line. This is more consistent with the
+ alignment behaviour for multi-line paragraphs in general. Issue #2425. PR #2427.
+* Tags will now be shown in the Novel View tooltip pop-out under the triangle button, together with
+ all the other meta data collected about a document or heading. PR #2429.
+
+**Documentation**
+
+* The documentation on how alignment and first line indentation works in conjunction with
+ in-paragraph line breaks, the setting to keep or ignore such line breaks, and the forced line
+ break shortcode, has been improved. Issue #2425. PR #2427.
+
+----
+
## Version 2.7.1 [2025-06-09]
### Release Notes
diff --git a/novelwriter/__init__.py b/novelwriter/__init__.py
index 594dc30c..7e9f604a 100644
--- a/novelwriter/__init__.py
+++ b/novelwriter/__init__.py
@@ -49,9 +49,9 @@ __license__ = "GPLv3"
__author__ = "Veronica Berglyd Olsen"
__maintainer__ = "Veronica Berglyd Olsen"
__email__ = "code@vkbo.net"
-__version__ = "2.7.1"
-__hexversion__ = "0x020701f0"
-__date__ = "2025-06-09"
+__version__ = "2.7.2"
+__hexversion__ = "0x020702f0"
+__date__ = "2025-06-24"
__status__ = "Stable"
__domain__ = "novelwriter.io"
diff --git a/sample/nwProject.nwx b/sample/nwProject.nwx
index c53ff1c9..d25301aa 100644
--- a/sample/nwProject.nwx
+++ b/sample/nwProject.nwx
@@ -1,6 +1,6 @@
-
-
+
+
Sample Project
Jane Smith
From 4dd2d63d9bd6755581529d8e1045480286239240 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Tue, 24 Jun 2025 16:40:25 +0200
Subject: [PATCH 17/21] Update changelog
---
CHANGELOG.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 209aba53..b7c7a039 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -16,8 +16,6 @@ displaying tags for novel documents on the user interface.
documents, and in previews. Issue #2410. PR #2411.
* Fixed an issue where titles with line breaks in them would have page breaks applied to both lines
for preview and PDF documents. Issue #2415. PR #2416.
-* When comments are enabled in the viewer, story comments should also be visible. A separate filter
- button will be added for this in 2.8. PR #2420.
* Fixed an issue where the justified text setting would not be properly applied to a paragraph in a
manuscript document if there was a line break in the paragraph, but single line breaks were set
to be ignored. Issue #2426. PR #2427.
@@ -26,6 +24,8 @@ displaying tags for novel documents on the user interface.
**Improvements**
+* When comments are enabled in the viewer, story comments should also be visible. A separate filter
+ button will be added for this in 2.8. PR #2420.
* When a paragraph has line breaks in it, the alignment tag will now override first line
indentation even if the alignment tag is not on the first line. This is more consistent with the
alignment behaviour for multi-line paragraphs in general. Issue #2425. PR #2427.
From 5b24d8e61a73d28329f6898fa554d0dbe0dde930 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Tue, 24 Jun 2025 17:16:39 +0200
Subject: [PATCH 18/21] Fix merge issue
---
novelwriter/gui/docviewer.py | 2 --
1 file changed, 2 deletions(-)
diff --git a/novelwriter/gui/docviewer.py b/novelwriter/gui/docviewer.py
index ec002e9f..d5445536 100644
--- a/novelwriter/gui/docviewer.py
+++ b/novelwriter/gui/docviewer.py
@@ -228,8 +228,6 @@ class GuiDocViewer(QTextBrowser):
qDoc.setTheme(self._docTheme)
qDoc.initDocument()
qDoc.setKeywords(True)
- qDoc.setCommentType(nwComment.NOTE, CONFIG.viewComments)
- qDoc.setCommentType(nwComment.STORY, CONFIG.viewComments)
qDoc.setCommentType(nwComment.PLAIN, CONFIG.viewComments)
qDoc.setCommentType(nwComment.SYNOPSIS, CONFIG.viewSynopsis)
qDoc.setCommentType(nwComment.SHORT, CONFIG.viewSynopsis)
From d1c3643cff2a6d30003d6f7303849b23801c5cd0 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Wed, 2 Jul 2025 17:41:41 +0200
Subject: [PATCH 19/21] Add all used licenses to package meta
---
pyproject.toml | 2 +-
setup/LICENSE-Apache-2.0.txt | 202 +++++++++++++++++++++++++++++++++++
2 files changed, 203 insertions(+), 1 deletion(-)
create mode 100644 setup/LICENSE-Apache-2.0.txt
diff --git a/pyproject.toml b/pyproject.toml
index 9263eb81..5dc6e443 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -9,7 +9,7 @@ authors = [
]
description = "A plain text editor for planning and writing novels"
readme = {file = "setup/description_pypi.md", content-type = "text/markdown"}
-license = {text = "GPL-3.0-or-later"}
+license = {text = "GPL-3.0-or-later AND Apache-2.0 AND CC-BY-4.0"}
classifiers = [
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.10",
diff --git a/setup/LICENSE-Apache-2.0.txt b/setup/LICENSE-Apache-2.0.txt
new file mode 100644
index 00000000..7a4a3ea2
--- /dev/null
+++ b/setup/LICENSE-Apache-2.0.txt
@@ -0,0 +1,202 @@
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
\ No newline at end of file
From 849f6c7d05c4592a7972bb80ad8f7c248fc98a60 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Wed, 2 Jul 2025 17:42:57 +0200
Subject: [PATCH 20/21] Update credits files
---
CREDITS.md | 30 +++++++++++++++++------
novelwriter/assets/text/credits_en.htm | 34 ++++++++++++++++++++------
2 files changed, 48 insertions(+), 16 deletions(-)
diff --git a/CREDITS.md b/CREDITS.md
index b3c527a5..4c08b186 100644
--- a/CREDITS.md
+++ b/CREDITS.md
@@ -55,19 +55,33 @@ The following libraries are dependencies of novelWriter:
* **Enchant** by Dom Lachowicz
* **PyEnchant** by Dimitri Merejkowsky
-## Assets
+## Icon Themes
-Some of the assets bundled with novelWriter were adapted from the following sources:
+The following icon themes are bundles with novelWriter:
* **Material Symbols** icons by Google Inc (Apache 2.0)
* **Remix** icons by RemixIcon (Apache 2.0)
* **Font Awesome** icons by Fonticons Inc (CC BY 4.0)
-* **Tomorrow** themes by Chris Kempson (MIT License)
-* **Owl** themes by Sarah Drasner (MIT License)
-* **Solarized** themes by Ethan Schoonover (MIT License)
-* **Cyberpunk Night** theme by Anders Lemvigh (CC BY-SA 4.0)
-* **Dracula** theme by Zeno Rocha (MIT License)
-* **Snazzy Light** theme by Florian Reuschel (MIT License)
+
+## Colour Themes
+
+Some colour themes included with novelWriter were adapted from the following sources:
+
+* **Dracula** theme by Zeno Rocha
+* **Espresso** theme by Sainnhe Park and Antoine Cotten
+* **Noctis** themes by Liviu Schera, and contributors
+* **Owl** themes by Sarah Drasner
+* **Primer** themes by GitHub Inc and Myian
+* **Snazzy Light** theme by Florian Reuschel
+* **Solarized** themes by Ethan Schoonover
+* **Tango** themes by the Tango Desktop Project
+* **Tomorrow** themes by Chris Kempson
+
+Other themes were created for novelWriter:
+
+* **Blue Streak** themes by Veronica Berglyd Olsen
+* **Cyberpunk Night** theme by Anders Lemvigh
+* **LCARS** theme by Veronica Berglyd Olsen
## Fonts
diff --git a/novelwriter/assets/text/credits_en.htm b/novelwriter/assets/text/credits_en.htm
index 33903616..c8d868a9 100644
--- a/novelwriter/assets/text/credits_en.htm
+++ b/novelwriter/assets/text/credits_en.htm
@@ -67,20 +67,38 @@ more contributions are listed on the project's Members page.
PyEnchant by Dimitri Merejkowsky
-
Assets
+Icon Themes
-Some of the assets bundled with novelWriter were adapted from the following sources:
+The following icon themes are bundles with novelWriter:
- Material Symbols icons by Google Inc (Apache 2.0)
- Remix icons by RemixIcon (Apache 2.0)
- Font Awesome icons by Fonticons Inc (CC BY 4.0)
- - Tomorrow themes by Chris Kempson (MIT License)
- - Owl themes by Sarah Drasner (MIT License)
- - Solarized themes by Ethan Schoonover (MIT License)
- - Cyberpunk Night theme by Anders Lemvigh (CC BY-SA 4.0)
- - Dracula theme by Zeno Rocha (MIT License)
- - Snazzy Light theme by Florian Reuschel (MIT License)
+
+
+Colour Themes
+
+Some colour themes included with novelWriter were adapted from the following sources:
+
+
+ - Dracula theme by Zeno Rocha
+ - Espresso theme by Sainnhe Park and Antoine Cotten
+ - Noctis themes by Liviu Schera, and contributors
+ - Owl themes by Sarah Drasner
+ - Primer themes by GitHub Inc and Myian
+ - Snazzy Light theme by Florian Reuschel
+ - Solarized themes by Ethan Schoonover
+ - Tango themes by the Tango Desktop Project
+ - Tomorrow themes by Chris Kempson
+
+
+Other themes were created for novelWriter:
+
+
+ - Blue Streak themes by Veronica Berglyd Olsen
+ - Cyberpunk Night theme by Anders Lemvigh
+ - LCARS theme by Veronica Berglyd Olsen
Fonts
From 168bb0be88e5c89f02608be12ab50947b95f6cd6 Mon Sep 17 00:00:00 2001
From: Veronica Berglyd Olsen <1619840+vkbo@users.noreply.github.com>
Date: Wed, 2 Jul 2025 17:55:06 +0200
Subject: [PATCH 21/21] Update license fields in project file to latest format
---
pyproject.toml | 6 +++++-
1 file changed, 5 insertions(+), 1 deletion(-)
diff --git a/pyproject.toml b/pyproject.toml
index 5dc6e443..3610e012 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -9,7 +9,11 @@ authors = [
]
description = "A plain text editor for planning and writing novels"
readme = {file = "setup/description_pypi.md", content-type = "text/markdown"}
-license = {text = "GPL-3.0-or-later AND Apache-2.0 AND CC-BY-4.0"}
+license = "GPL-3.0-or-later AND Apache-2.0 AND CC-BY-4.0"
+license-files = [
+ "LICENSE.md",
+ "setup/LICENSE-Apache-2.0.txt",
+]
classifiers = [
"Programming Language :: Python :: 3 :: Only",
"Programming Language :: Python :: 3.10",