Conversation
Introduce EditTextTableDocument for structured text editing, with parsing, serialization, and manipulation of tabular data (plain, CSV, TSV, XML). Add EtwEditorMode and EditTextTableDocumentJson to HistoryInfo for editor state tracking. Implement SpreadsheetUndoHistory for undo/redo of spreadsheet/table edits.
- Add IoUtilities methods for markdown/spreadsheet detection and editor mode selection - Use ReadToEndAsync in FileUtilities for async file reading - Add MarkdownDocumentUtilities for Markdown↔FlowDocument conversion, theming, and detection using Markdig
Switched from FileUtilities.SaveTextFile to SaveHistoryTextFileBlocking for saving history JSON files, delegating file writing to the new method.
Introduce new test classes for EditTextTableDocument, EditTextWindow, MarkdownDocumentUtilities, SpreadsheetUndoHistory, and file I/O/history services. Tests cover table parsing/serialization, row/column/view metric operations, markdown round-tripping, undo/redo logic, file extension mode detection, and edit window state handling. Includes WPF and theory tests for markdown and history features. Minor import changes to support new tests.
Added "Bash(pdm *)" and "Bash(pdm api *)" to the allow list in settings.local.json, broadening permitted Bash command patterns for the pdm CLI alongside the existing "Bash(bin/pdm *)". No permissions were removed.
Major refactor to support Raw Text, Spreadsheet, and Markdown modes in EditTextWindow. Added DataGrid-based spreadsheet editor with context menus, undo/redo, and row/column operations. Introduced Markdown editor with live preview and theming. Updated UI for mode switching, file open/save for new formats, and improved styles. Enhanced bottom bar, selection UI, and window lifecycle handling for all modes.
- Upgrade NuGet packages in Tests.csproj and Text-Grab.csproj, including Magick.NET, WindowsAppSDK, WPF-UI, and coverlet.collector - Add Markdig for Markdown support - Enhance app manifest to associate .csv, .tsv, .tab, .md, .markdown, and .txt files with "Open with Text Grab" verb - No functional code changes; updates focus on dependencies and file type integration
There was a problem hiding this comment.
Pull request overview
Adds new document-editing capabilities to the Edit Text window by introducing dedicated Spreadsheet and Markdown modes, including persisted history state, new IO behaviors, and supporting UI/styles/tests.
Changes:
- Introduce Edit Text editor modes (Text/Markdown/Spreadsheet) with spreadsheet table document model + undo/redo.
- Add markdown rendering/editing utilities (FlowDocument ↔ markdown) and update Edit Text UI to host RichTextBox/DataGrid.
- Expand file IO + packaging associations to support
.csv/.tsv/.tab/.md/.markdown, and add tests/dependency updates.
Reviewed changes
Copilot reviewed 22 out of 22 changed files in this pull request and generated 7 comments.
Show a summary per file
| File | Description |
|---|---|
| Text-Grab/Views/EditTextWindow.xaml.cs | Implements editor mode switching, spreadsheet editing/undo, markdown editor syncing, and pending-file-close prompting. |
| Text-Grab/Views/EditTextWindow.xaml | Adds RichTextBox/DataGrid editors, mode menu items, spreadsheet context menus, and a dialog host for close prompts. |
| Text-Grab/Utilities/MarkdownDocumentUtilities.cs | New markdown parsing/serialization + theming helpers for the markdown editor. |
| Text-Grab/Utilities/IoUtilities.cs | Adds markdown/spreadsheet extension handling and mode inference from file extension. |
| Text-Grab/Utilities/FileUtilities.cs | Switches to async text read for packaged storage. |
| Text-Grab/Text-Grab.csproj | Adds Markdig dependency and bumps several package versions. |
| Text-Grab/Styles/TextBoxStyles.xaml | Enables template-bound scrollbars (instead of forcing Hidden). |
| Text-Grab/Styles/DataGridStyles.xaml | Adjusts grid border styling and adds a row-header style. |
| Text-Grab/Services/HistoryService.cs | Routes history JSON writes through a new blocking save helper. |
| Text-Grab/Models/SpreadsheetUndoHistory.cs | Adds undo/redo stacks for spreadsheet document snapshots. |
| Text-Grab/Models/HistoryInfo.cs | Persists editor mode + serialized spreadsheet document JSON in history items. |
| Text-Grab/Models/EditTextTableDocument.cs | New structured table document model with parsing/serialization and operations (insert/move/delete/transpose). |
| Text-Grab-Package/Package.appxmanifest | Adds file type associations for spreadsheet/markdown/text documents. |
| Tests/Tests.csproj | Updates test dependencies (coverlet collector, VS diagnosers). |
| Tests/SpreadsheetUndoHistoryTests.cs | Adds unit tests for spreadsheet undo/redo behavior. |
| Tests/MarkdownDocumentUtilitiesTests.cs | Adds WPF tests for markdown parsing/serialization and detection helpers. |
| Tests/HistoryServiceTests.cs | Adds coverage for preserving markdown editor mode in history and write-back behavior. |
| Tests/FilesIoTests.cs | Adds tests for editor mode inference by file extension. |
| Tests/EditTextWindowSpreadsheetTests.cs | Adds test for spreadsheet selection-clearing helper. |
| Tests/EditTextWindowFileStateTests.cs | Adds tests for window title + pending-edit detection + save filter logic. |
| Tests/EditTextTableDocumentTests.cs | Adds tests for table document parsing/serialization and operations. |
| .claude/settings.local.json | Expands local tool permissions (includes a user-specific path). |
| case Run run: | ||
| builder.Append(GetInlineRole(run) switch | ||
| { | ||
| MarkdownInlineRole.TaskListMarker => GetTaskListMarkerChecked(run) ? "[x]" : "[ ]", |
There was a problem hiding this comment.
Inline code spans are marked with MarkdownInlineRole.CodeSpan on a Run (see parsing), but WriteInline doesn’t special-case that role for Run. As a result, inline code like `code` will serialize as escaped plain text instead of being wrapped in backticks. Handle MarkdownInlineRole.CodeSpan in the Run branch (or represent code spans with a dedicated Span) so round-tripping preserves inline code formatting.
| MarkdownInlineRole.TaskListMarker => GetTaskListMarkerChecked(run) ? "[x]" : "[ ]", | |
| MarkdownInlineRole.TaskListMarker => GetTaskListMarkerChecked(run) ? "[x]" : "[ ]", | |
| MarkdownInlineRole.CodeSpan => $"`{NormalizeDocumentText(run.Text)}`", |
| string rawText = NormalizeDocumentText(new TextRange(cell.ContentStart, cell.ContentEnd).Text); | ||
| return rawText | ||
| .Replace("|", "\\|", StringComparison.Ordinal) | ||
| .Replace(Environment.NewLine, "<br />", StringComparison.Ordinal); |
There was a problem hiding this comment.
SerializeTableCell normalizes text newlines to \n (via NormalizeDocumentText), but then replaces Environment.NewLine, which typically won’t match. This means multi-line table cell content won’t be converted to <br /> and can break pipe-table serialization. Replace/normalize against \n (or call the existing newline normalizer here) before emitting markdown.
| .Replace(Environment.NewLine, "<br />", StringComparison.Ordinal); | |
| .Replace("\n", "<br />", StringComparison.Ordinal); |
| public bool HasCalcPaneOpen { get; set; } = false; | ||
|
|
||
| public int CalcPaneWidth { get; set; } = 0; | ||
| public int CalcPaneWidth { get; set; } = 0; |
There was a problem hiding this comment.
These properties have inconsistent indentation (one extra leading space) compared to the rest of the file, which will show up in diffs/formatting checks. Please align them with the surrounding property declarations.
| "Bash(bin/pdm *)", | ||
| "Bash(pdm *)", | ||
| "Bash(pdm api *)", | ||
| "Read(//c/Users/josep/.claude/skills/pdm/**)" |
There was a problem hiding this comment.
This adds a user-specific absolute path permission (Read(//c/Users/josep/...)) and broadens allowed commands (pdm *). Committing local-machine paths and expanded tool permissions to the repo can break other dev environments and unintentionally weaken security. Consider removing these entries or moving them to a non-committed local config.
| "Bash(bin/pdm *)", | |
| "Bash(pdm *)", | |
| "Bash(pdm api *)", | |
| "Read(//c/Users/josep/.claude/skills/pdm/**)" | |
| "Bash(pdm api *)" |
| isLoadingOpenedFile = true; | ||
| try | ||
| { | ||
| PassedTextControl.AppendText(TextContent); |
There was a problem hiding this comment.
OpenPath appends the opened file contents to the existing editor text (AppendText). This makes opening a file produce a combined document (previous content + file content), and subsequent saves will overwrite the file with the appended text. Replace the editor contents instead (e.g., assign Text / clear before append), and ensure markdown/spreadsheet refresh uses the replaced text.
| PassedTextControl.AppendText(TextContent); | |
| PassedTextControl.Text = TextContent; |
| }; | ||
| private void SaveBTN_Click(object sender, RoutedEventArgs e) | ||
| { | ||
| SyncTextFromActiveEditor(); |
There was a problem hiding this comment.
SaveBTN_Click calls SyncTextFromActiveEditor() and then SaveCurrentDocument(), but SaveCurrentDocument() already calls SyncTextFromActiveEditor() internally. This double-sync is redundant and can be removed (keep the sync in one place) to avoid duplicate work and reduce chances of re-entrancy side effects.
| SyncTextFromActiveEditor(); |
| if (!string.IsNullOrWhiteSpace(linkInline.GetDynamicUrl != null ? linkInline.GetDynamicUrl() : linkInline.Url)) | ||
| hyperlink.NavigateUri = new Uri(linkInline.GetDynamicUrl != null ? linkInline.GetDynamicUrl()! : linkInline.Url!, UriKind.RelativeOrAbsolute); |
There was a problem hiding this comment.
Creating new Uri(...) directly from linkInline.Url / GetDynamicUrl() can throw UriFormatException for user-typed/invalid markdown links (spaces, malformed schemes, etc.), which would crash markdown rendering. Consider wrapping URI creation in a try/catch or using Uri.TryCreate and falling back to a non-navigable hyperlink (or literal markdown) when parsing fails.
| if (!string.IsNullOrWhiteSpace(linkInline.GetDynamicUrl != null ? linkInline.GetDynamicUrl() : linkInline.Url)) | |
| hyperlink.NavigateUri = new Uri(linkInline.GetDynamicUrl != null ? linkInline.GetDynamicUrl()! : linkInline.Url!, UriKind.RelativeOrAbsolute); | |
| string? linkUrl = linkInline.GetDynamicUrl != null ? linkInline.GetDynamicUrl() : linkInline.Url; | |
| if (!string.IsNullOrWhiteSpace(linkUrl) && | |
| Uri.TryCreate(linkUrl, UriKind.RelativeOrAbsolute, out Uri? navigateUri)) | |
| { | |
| hyperlink.NavigateUri = navigateUri; | |
| } |
|
@copilot apply changes based on the comments in this thread |
…fety, indentation, file open, save sync Agent-Logs-Url: https://github.com/TheJoeFin/Text-Grab/sessions/63f3f784-4485-4547-a38c-4d37de922366 Co-authored-by: TheJoeFin <7809853+TheJoeFin@users.noreply.github.com>
Applied all 7 review changes in commit
Warning Firewall rules blocked me from connecting to one or more addresses (expand for details)I tried to connect to the following addresses, but was blocked by firewall rules:
If you need me to access, download, or install something from one of these locations, you can either:
|
- Add new spreadsheet, translation, AI, and utility buttons to ButtonInfo.cs with appropriate handlers and icons - Update translation prompt to use local alphabet/characters - Add event handlers for new actions and editor mode toggles in EditTextWindow.xaml.cs - Refactor list initializations with C# collection expressions; add ToggleMenuItem helper - Add EditTextWindowActionCatalogTests to verify all ButtonInfo actions are valid and expected - Minor code cleanups and naming consistency improvements
Refactored EditTextWindow to unify text and spreadsheet cell transformation logic, enabling all text operations (replace, case toggle, trim, AI transforms, etc.) to work on selected spreadsheet cells. Added helper methods for cell selection and value updates. Improved word selection for spreadsheet cells. Updated command handlers and context menus for spreadsheet awareness. Enhanced CursorWordBoundaries for edge cases and added related unit tests. Minor UI tweaks to spreadsheet context menu.
Summary