→ PYRIGHT_PYTHON_FORCE_VERSION=latest pyright
added 1 package, and audited 2 packages in 4s
found 0 vulnerabilities
/home/io/Projects/textual-paint/src/textual_paint/paint.py
/home/io/Projects/textual-paint/src/textual_paint/paint.py:1173:64 - error: Condition will always evaluate to True since the types "Never" and "None" have no overlap (reportUnnecessaryComparison)
/home/io/Projects/textual-paint/src/textual_paint/paint.py:1214:26 - error: Pattern will never be matched for subject type "Never" (reportUnnecessaryComparison)
/home/io/Projects/textual-paint/src/textual_paint/paint.py:1217:26 - error: Pattern will never be matched for subject type "Never" (reportUnnecessaryComparison)
/home/io/Projects/textual-paint/src/textual_paint/paint.py:1220:26 - error: Pattern will never be matched for subject type "Never" (reportUnnecessaryComparison)
/home/io/Projects/textual-paint/src/textual_paint/paint.py:1223:26 - error: Pattern will never be matched for subject type "Never" (reportUnnecessaryComparison)
/home/io/Projects/textual-paint/src/textual_paint/paint.py:1229:26 - error: Pattern will never be matched for subject type "Never" (reportUnnecessaryComparison)
/home/io/Projects/textual-paint/src/textual_paint/paint.py:1233:26 - error: Pattern will never be matched for subject type "Never" (reportUnnecessaryComparison)
7 errors, 0 warnings, 0 informations
While Pyright can narrow down the type of format_id from `str | None` to `str` based on `format_id in Image.SAVE` (where `Image.SAVE` is `dict[str, Any]`), mypy does not.
When opening the Open dialog, it shouldn't populate the file name input,
but when clicking a file, it should fill in the clicked file's name.
The `_expanding_directory_tree` flag gave false positives:
- When opening a file in a directory the program doesn't have permissions to via the CLI,
such as with `python -m src.textual_paint.paint /root/nonexistent`,
opening the Open dialog, and clicking a file, it wasn't populating the filename input,
because the flag was never cleared if it couldn't find (access) the
directories and file matching any of the path parts, working down the directory structure.
This was broken in [b3ca55a3b1] "Use new callback to remove race condition"
but before, it just cleared the flag after a delay, so it wasn't a good solution,
It also gave false negatives:
- When opening the Open dialog, it was populating the name input field
with the file selected programmatically with `expand_to_path`,
instead of leaving it blank.
The automatic expansion was broken in [e9755637d6] "Update Textual to 0.26.0"
due to changes 0.25.0, since directory contents are now loaded in a worker.
This flag's behavior was broken since rewriting the code to handle asynchronous directory loading in
[4e1f11ab23] "Fix expanding directory tree to current folder, in file dialogs"
An alternative I considered was to create a new message such as `EnhancedDirectoryTree.NodeHighlighted`, but let's call it `EnhancedNodeHighlighted`, then to have `on_tree_node_highlighted` in `EnhancedDirectoryTree` which sends `EnhancedNodeHighlighted` always with `from_expand_to_path=False`, and then do:
| # Suppress EnhancedNodeHighlighted with from_expand_to_path=False
| with self.prevent(self.EnhancedNodeHighlighted):
| self.select_node(node)
| # Send it with the flag set
| self.post_message(self.EnhancedNodeHighlighted(..., from_expand_to_path=True))
The solution I settled on was to add an `on_tree_node_highlighted` method to my `EnhancedDirectoryTree` for the purpose of clearing the flag, with still a further delay so that the app's `on_tree_node_highlighted` can use the flag before its cleared.
(I left `_expanding_directory_tree` in at this commit for comparison of the behavior. For a fair estimate of the complexity of this change, include the following cleanup in the diff.)
If digits were present in the document, they were concatenated after
the color codes, and they ran together indistinguishably.
This changed the color index numbers that were read back, to potentially
invalid color indices, and lost the digits as part of the document.
It needs the delimiter of the ending escape code to separate the digits.
When attempting to save in a format only supported for opening,
it tripped the assertion here, which I'm removing.
(This code was a little complicated to reason about, and I thought I was
oversimplifying, when restructuring to handle the overlap of warnings,
but I couldn't place what case I was missing, until testing this.)
This avoids duplicately detecting the format from the file extension
in one case, and in the other case, avoids using a bogus file name to
implement a default fallback file format.
Of course, this highlights the need to prompt during Save as well,
not just Save As, or to make creating color or character information
impossible depending on the type of file that is open.
Can't use self.open_from_file_path because:
1. it short-circuits if the file is already open
2. it clears the undo history*
3. it does stuff with the backup file
4. plus it's not as efficient to use the FS, and it uses a callback,
which is a pain.
*TODO: make the reload undoable
Update style traces when styles are changed with `merge`/`merge_rules`.
I wish `merge` used `merge_rules` so I could define just one extra
instrumentation point. I guess I could make my `merge` use my version of
`merge_rules`, but that feels weird, even if it's probably not any more
fragile to implement it that way.
Using .merge() isn't tracked by the call stack recording (yet), so it
can no longer attribute inline styles to editing with the inspector,
but this lack of tracking was already a problem, it just highlights it.
The old screenshot was generated by Select All and Copy As HTML in
Ubuntu's Terminal app (using a keyboard shortcut that had to be set up
first), and post-processed using code included in screenshot.svg, which
I'm now deleting.
The new screenshot is generated using Textual's built-in SVG export.
It displays nicer, with less artifacts (seams between cells).
It doesn't need such silly explanation of the nature of the screenshot,
and was also sizing to the width of the text, so I removed the wrapper
table which was imitating (standing in for) figure/figcaption elements.
The new screenshot also includes a window border, macOS-styled, which is
a little weird since it's a remake of MS Paint (Windows software)
developed entirely on Ubuntu (Linux distro).
- This regressed due to updates in Textual 0.25.0,
because DirectoryTree now loads directory contents in a worker:
https://github.com/Textualize/textual/issues/2456
- Directory tree expansion may be more robust now, although it's using
more internals now, and it still needs timers for whatever reason.
Originally it was `paint.py`, but after switching to running as a module
(needed to use Python's relative imports for some reason) it became "-c"
when running with the `textual` CLI.
- Use very heavy solidus for paint brushes. This renders in a strange
overlapped way in Ubuntu Terminal and VS Code's terminal, which
actually works really well here. I just hope it doesn't become
a wide character in the future, or isn't treated as such on other
terminals.
- Add brush tips to the other brushes, each using a different character.
- Remove top border, since it conflicts with brush tips.
`widget.styles.tint` will return a default transparent (#00000000)
value, rather than None, when not set.
`border` doesn't work the same as the others here, but I'm not using it,
so I can just disable the styles I'm not using.
I surveyed `def render_line` in the Textual codebase to find the right
property to get the `color` CSS property working for ResizeHandle:
- `self.styles`: wrong type
- `self.get_component_rich_style()`: would overcomplicate things
- `self.text_style`: wrong
- `self.rich_style`: right
Now I can use `:hover` styles, and restore some of the elegance of the
look of the inspector by hiding the resize handles until mouse over.
It was an overly general CSS rule, confusingly applying to Tab,
because it's a *subclass* of Static.
Inspector features that would make this easier to understand would be:
- Showing what rules apply to a given widget, instead of just a CSS dump
- Showing better why exactly it was invisible
Regarding "it seems to be reset anyways": I believe it was simply
highlighting an incorrect node, often, by chance, an invisible one.
`self._get_node(-1)` doesn't return None.