Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .claude/skills/ui-system/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Components never pick their own values. Every color, spacing, radius, font, shad
| Scrollbar | `--scrollbar-thumb-color`, `--scrollbar-thumb-hover-color`, `--scrollbar-track-color` | theme |
| Text selection (editor, preview, inputs) | `--selection-bg` | theme |
| Search match highlight / search-in-selection scope in the editor | `--search-match-bg`, `--search-scope-bg` | theme |
| Mark All highlight, the five token styles, smart highlighting and the bookmark dot in the gutter | `--mark-found-bg`, `--mark-1-bg` … `--mark-5-bg`, `--smart-highlight-bg`, `--bookmark-color` | theme |
| Code tokens | `--keyword-color`, `--type-color`, `--attribute-color`, `--string-color`, `--number-color`, `--comment-color`, `--variable-color`, `--block-color`, `--xml-tag-color` | theme |
| Tag palette | `--tag-1-color` … `--tag-8-color` (theme), `--tag-1-bg` … `--tag-8-bg` (derived in base) | theme + base |
| Spacing scale | `--space-1` … `--space-6` | base |
Expand Down
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ A lightweight, fast and multi-functional desktop text editor for Windows, macOS

- Your tabs, including unsaved content, cursor position, scroll position and undo history, are saved automatically and restored on the next start.
- Right-click a tab for Close, Close Other Tabs, Close Unmodified Tabs, Close All Tabs, Pin/Unpin, Open Containing Folder and Reopen Closed Tab.
- Right-click in the editor for Undo/Redo, Cut/Copy/Paste/Delete, Select All, Base64, Format, line and case tools, Find, Replace, Go To, Search on the Web, Markdown/HTML preview, Open Containing Folder and Copy File Path. User plugin actions appear there too.
- Right-click in the editor for Undo/Redo, Cut/Copy/Paste/Delete, Select All, Base64, Format, line and case tools, Find, Replace, Go To, Search on the Web, Markdown/HTML preview, Toggle Bookmark, Style Token / Clear Style, Open Containing Folder and Copy File Path. User plugin actions appear there too.
- Pinned tabs stay at the front and cannot be closed by accident.
- Drag tabs to reorder them.
- Reopen up to 20 recently closed tabs with their content.
Expand Down Expand Up @@ -55,6 +55,10 @@ A lightweight, fast and multi-functional desktop text editor for Windows, macOS
- Replace All with "Folder" chosen asks first, then changes the files on disk and keeps each file's encoding and line endings. Files that are open in tabs are changed in the editor instead, so the change can still be undone. A file whose encoding cannot hold the replacement is left alone and listed in a notice.
- Go To (`Ctrl+G`) jumps to a line (`120`), a line and column (`120:15`) or a character offset (`#3456`). Go to Matching Brace is `Ctrl+B`.
- Search on the Web opens the selection in your browser; the search address is set in Settings > Search.
- Mark All (in the search bar or the Search menu) highlights every match of the search in the current file and keeps the highlight after the bar is closed; Clear Marks removes it. "Mark All and Bookmark Lines" also bookmarks each matching line.
- Style Token colors every occurrence of the selection, or the word at the cursor, in one of five styles (Search > Style Token, or right-click). Clear Style removes one style or all of them. Marks and styles follow your edits and are kept with the tab.
- Bookmarks: `Ctrl+F2` toggles a bookmark on the current line, `F2` / `Shift+F2` go to the next and previous bookmark. Search > Bookmark can also clear or invert them and cut, copy or remove the bookmarked (or the other) lines in a single undo step. Bookmarks move with the text and are kept with the tab.
- Smart highlighting: select a whole word (for example with a double-click) and its other occurrences are highlighted. It can be turned off in Settings > Search.

**Encoding and line endings**

Expand Down Expand Up @@ -98,6 +102,8 @@ On macOS, use `Cmd` instead of `Ctrl`. All of these can be changed in Settings >
| Find in files | `Ctrl+Alt+F` (`Cmd+Alt+Shift+F` on macOS) |
| Next / previous search result | `F4` / `Shift+F4` |
| Show or hide search results | `F7` |
| Toggle bookmark | `Ctrl+F2` |
| Next / previous bookmark | `F2` / `Shift+F2` |
| Go to line, column or offset | `Ctrl+G` |
| Go to matching brace | `Ctrl+B` |
| Format | `Ctrl+Shift+F` |
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "note-manager",
"productName": "Note Manager",
"version": "1.4.0",
"version": "1.5.0",
"description": "My Electron application description",
"main": ".webpack/main/index.cjs",
"scripts": {
Expand Down
8 changes: 8 additions & 0 deletions resources/Themes/Dark.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@
--selection-bg: #264f78;
--search-match-bg: rgba(209, 154, 42, 0.4);
--search-scope-bg: rgba(30, 144, 255, 0.12);
--mark-found-bg: rgba(229, 83, 75, 0.35);
--mark-1-bg: rgba(57, 168, 176, 0.35);
--mark-2-bg: rgba(224, 130, 61, 0.35);
--mark-3-bg: rgba(198, 144, 38, 0.35);
--mark-4-bg: rgba(163, 113, 247, 0.35);
--mark-5-bg: rgba(219, 97, 162, 0.35);
--smart-highlight-bg: rgba(63, 164, 91, 0.25);
--bookmark-color: #4493f8;
--active-line-bg: rgba(0, 0, 0, 0.48);
--action-bg: rgb(57, 57, 57);

Expand Down
8 changes: 8 additions & 0 deletions resources/Themes/Light.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@
--selection-bg: #add6ff;
--search-match-bg: rgba(255, 196, 0, 0.45);
--search-scope-bg: rgba(30, 144, 255, 0.1);
--mark-found-bg: rgba(207, 34, 46, 0.3);
--mark-1-bg: rgba(27, 124, 131, 0.3);
--mark-2-bg: rgba(188, 76, 0, 0.3);
--mark-3-bg: rgba(154, 103, 0, 0.3);
--mark-4-bg: rgba(130, 80, 223, 0.3);
--mark-5-bg: rgba(191, 57, 137, 0.3);
--smart-highlight-bg: rgba(26, 127, 55, 0.22);
--bookmark-color: #0969da;
--active-line-bg: rgba(147, 147, 147, 0.28);
--action-bg: rgb(220, 220, 220);

Expand Down
8 changes: 8 additions & 0 deletions resources/Themes/Matrix.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@
--selection-bg: rgba(0, 255, 65, 0.22);
--search-match-bg: rgba(212, 225, 87, 0.35);
--search-scope-bg: rgba(0, 212, 58, 0.1);
--mark-found-bg: rgba(255, 79, 79, 0.35);
--mark-1-bg: rgba(61, 255, 216, 0.35);
--mark-2-bg: rgba(255, 165, 61, 0.35);
--mark-3-bg: rgba(230, 255, 61, 0.35);
--mark-4-bg: rgba(185, 139, 255, 0.35);
--mark-5-bg: rgba(255, 95, 210, 0.35);
--smart-highlight-bg: rgba(0, 255, 65, 0.25);
--bookmark-color: #3db4ff;
--active-line-bg: rgba(93, 126, 76, 0.3);
--action-bg: rgb(40, 43, 32);

Expand Down
8 changes: 8 additions & 0 deletions resources/Themes/Orange Dark.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@
--selection-bg: rgba(227, 119, 42, 0.3);
--search-match-bg: rgba(242, 179, 61, 0.38);
--search-scope-bg: rgba(227, 119, 42, 0.12);
--mark-found-bg: rgba(229, 83, 75, 0.35);
--mark-1-bg: rgba(79, 179, 165, 0.35);
--mark-2-bg: rgba(255, 140, 66, 0.35);
--mark-3-bg: rgba(242, 179, 61, 0.35);
--mark-4-bg: rgba(196, 140, 230, 0.35);
--mark-5-bg: rgba(224, 103, 154, 0.35);
--smart-highlight-bg: rgba(139, 191, 79, 0.25);
--bookmark-color: #5d9bd5;
--active-line-bg: rgb(71, 58, 51);
--action-bg: #5a2e0b;

Expand Down
8 changes: 8 additions & 0 deletions resources/Themes/Soft Light.css
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@
--selection-bg: #b5e3d8;
--search-match-bg: rgba(232, 168, 0, 0.4);
--search-scope-bg: rgba(31, 138, 120, 0.1);
--mark-found-bg: rgba(196, 49, 75, 0.3);
--mark-1-bg: rgba(19, 114, 100, 0.3);
--mark-2-bg: rgba(181, 76, 23, 0.3);
--mark-3-bg: rgba(138, 90, 0, 0.3);
--mark-4-bg: rgba(123, 82, 201, 0.3);
--mark-5-bg: rgba(176, 58, 122, 0.3);
--smart-highlight-bg: rgba(46, 125, 50, 0.22);
--bookmark-color: #165caf;
--active-line-bg: rgba(147, 147, 147, 0.28);
--action-bg: #b8d8d0;

Expand Down
54 changes: 53 additions & 1 deletion src/BaseTheme.css
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@
--selection-bg: var(--action-bg);
--search-match-bg: color-mix(in srgb, var(--warning-color) 40%, transparent);
--search-scope-bg: color-mix(in srgb, var(--positive-color) 10%, transparent);
--mark-found-bg: color-mix(in srgb, var(--tag-1-color) 35%, transparent);
--mark-1-bg: color-mix(in srgb, var(--tag-5-color) 35%, transparent);
--mark-2-bg: color-mix(in srgb, var(--tag-2-color) 35%, transparent);
--mark-3-bg: color-mix(in srgb, var(--tag-3-color) 35%, transparent);
--mark-4-bg: color-mix(in srgb, var(--tag-7-color) 35%, transparent);
--mark-5-bg: color-mix(in srgb, var(--tag-8-color) 35%, transparent);
--smart-highlight-bg: color-mix(in srgb, var(--tag-4-color) 25%, transparent);
--bookmark-color: var(--positive-color);
--scrollbar-thumb-hover-color: var(--scrollbar-thumb-color);
--scrollbar-track-color: transparent;

Expand Down Expand Up @@ -300,7 +308,7 @@ body .cet-menubar-menu-container .cet-action-menu-item {
z-index: var(--z-tooltip) !important;
}

.ace_marker-layer .searchMatch, .ace_marker-layer .searchScope {
.ace_marker-layer .searchMatch, .ace_marker-layer .searchScope, .ace_marker-layer .mark, .ace_marker-layer .smartHighlight {
position: absolute;
z-index: var(--z-editor-marker);
}
Expand All @@ -314,6 +322,50 @@ body .cet-menubar-menu-container .cet-action-menu-item {
background-color: var(--search-scope-bg);
}

.ace_marker-layer .mark, .ace_marker-layer .smartHighlight {
border-radius: var(--radius-sm);
}

.ace_marker-layer .smartHighlight {
background-color: var(--smart-highlight-bg);
}

.ace_marker-layer .mark.found {
background-color: var(--mark-found-bg);
}

.ace_marker-layer .mark.style-1 {
background-color: var(--mark-1-bg);
}

.ace_marker-layer .mark.style-2 {
background-color: var(--mark-2-bg);
}

.ace_marker-layer .mark.style-3 {
background-color: var(--mark-3-bg);
}

.ace_marker-layer .mark.style-4 {
background-color: var(--mark-4-bg);
}

.ace_marker-layer .mark.style-5 {
background-color: var(--mark-5-bg);
}

.ace_gutter-cell.bookmark::before {
content: "";
position: absolute;
top: 50%;
left: var(--space-1);
width: 8px;
height: 8px;
border-radius: 50%;
background-color: var(--bookmark-color);
transform: translateY(-50%);
}

.ace_line-hover {
background-color: var(--selected-highlight-color) !important;
}
Expand Down
66 changes: 66 additions & 0 deletions src/domain/Bookmarks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
export interface LineChange {
action: "insert" | "remove",
start: {row: number, column: number},
end: {row: number, column: number},
}

export function toggleRow(rows: Array<number>, row: number): Array<number> {
return rows.includes(row) ? rows.filter(r => r !== row) : sortRows([...rows, row]);
}

export function findAdjacentRow(rows: Array<number>, current: number, isBackwards: boolean): number | undefined {
if (rows.length === 0) return undefined;

if (isBackwards) return [...rows].reverse().find(row => row < current) ?? rows[rows.length - 1];

return rows.find(row => row > current) ?? rows[0];
}

export function shiftRows(rows: Array<number>, change: LineChange): Array<number> {
const {start, end} = change;
const lineCount = end.row - start.row;

if (lineCount === 0 || rows.length === 0) return rows;

if (change.action === "insert") return rows.map(row => row > start.row || (row === start.row && start.column === 0) ? row + lineCount : row);

const removesWholeLines = start.column === 0 && end.column === 0;

return sortRows(rows.flatMap(row => {
if (row > end.row || (removesWholeLines && row === end.row)) return [row - lineCount];
if (row < start.row || (!removesWholeLines && row === start.row)) return [row];

return removesWholeLines ? [] : [start.row];
}));
}

export function invertRows(rows: Array<number>, lineCount: number): Array<number> {
const marked = new Set(rows);

return Array.from({length: lineCount}, (_, row) => row).filter(row => !marked.has(row));
}

export function pickLines(lines: Array<string>, rows: Array<number>, isMarked: boolean): Array<string> {
const marked = new Set(rows);

return lines.filter((line, row) => marked.has(row) === isMarked);
}

export function groupConsecutiveRows(rows: Array<number>): Array<[number, number]> {
return rows.reduce<Array<[number, number]>>((blocks, row) => {
const last = blocks[blocks.length - 1];

if (last && last[1] === row - 1) last[1] = row;
else blocks.push([row, row]);

return blocks;
}, []);
}

export function normalizeRows(raw: unknown): Array<number> {
return Array.isArray(raw) ? sortRows(raw.filter((row): row is number => Number.isInteger(row) && row >= 0)) : [];
}

function sortRows(rows: Array<number>): Array<number> {
return [...new Set(rows)].sort((a, b) => a - b);
}
3 changes: 3 additions & 0 deletions src/domain/EditorTab.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {SupportedLanguage} from "./SupportedLanguage";
import {EolType} from "./Encoding";
import {MarkRule} from "./Marks";

export interface EditorTab {
id?:string,
Expand All @@ -21,5 +22,7 @@ export interface EditorTab {
},
selection?: any,
undoHistory?: any,
bookmarks?: Array<number>,
marks?: Array<MarkRule>,
}
}
57 changes: 57 additions & 0 deletions src/domain/Marks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import {normalizeSearchOptions, SearchMode, SearchQuery} from "./Search";

export enum MarkStyle {
FOUND = "found",
STYLE_1 = "style-1",
STYLE_2 = "style-2",
STYLE_3 = "style-3",
STYLE_4 = "style-4",
STYLE_5 = "style-5",
}

export interface MarkRule {
style: MarkStyle,
query: SearchQuery,
}

export const TOKEN_STYLES = [MarkStyle.STYLE_1, MarkStyle.STYLE_2, MarkStyle.STYLE_3, MarkStyle.STYLE_4, MarkStyle.STYLE_5];

export function createTokenQuery(text: string): SearchQuery {
return {mode: SearchMode.NORMAL, matchCase: true, wholeWord: false, dotAll: false, wrap: true, text: text};
}

export function createSmartHighlightQuery(text: string): SearchQuery {
return {...createTokenQuery(text), matchCase: false, wholeWord: true};
}

export function addMarkRule(rules: Array<MarkRule>, rule: MarkRule): Array<MarkRule> {
const key = JSON.stringify(rule);

return [...rules.filter(existing => JSON.stringify(existing) !== key), rule];
}

export function clearMarkStyles(rules: Array<MarkRule>, styles: Array<MarkStyle>): Array<MarkRule> {
return rules.filter(rule => !styles.includes(rule.style));
}

export function normalizeMarkRules(raw: unknown): Array<MarkRule> {
if (!Array.isArray(raw)) return [];

return raw
.filter(rule => Object.values(MarkStyle).includes(rule?.style) && typeof rule?.query?.text === "string" && rule.query.text !== "")
.map(rule => ({style: rule.style, query: {...normalizeSearchOptions(rule.query), text: rule.query.text}}));
}

const WORD_CHARACTER = /[\p{L}\p{N}_]/u;
const WORD = /^[\p{L}\p{N}_]+$/u;

export function readWholeWord(line: string, start: number, end: number): string | undefined {
const text = line.substring(start, end);
const isBoundary = (character: string | undefined) => !character || !WORD_CHARACTER.test(character);

return WORD.test(text) && isBoundary(line[start - 1]) && isBoundary(line[end]) ? text : undefined;
}

export function getMarkClassName(style: MarkStyle): string {
return "mark " + style;
}
3 changes: 3 additions & 0 deletions src/domain/Settings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export interface FileSettings {

export interface SearchSettings {
webSearchUrl: string,
smartHighlight: boolean,
}

export interface Settings {
Expand Down Expand Up @@ -67,6 +68,7 @@ export const DEFAULT_SETTINGS: Settings = {
},
search: {
webSearchUrl: DEFAULT_WEB_SEARCH_URL,
smartHighlight: true,
},
keyBindings: {},
};
Expand Down Expand Up @@ -191,6 +193,7 @@ export function normalizeSettings(raw: any): Settings {
},
search: {
webSearchUrl: isWebSearchTemplate(search.webSearchUrl) ? search.webSearchUrl : defaults.search.webSearchUrl,
smartHighlight: toBoolean(search.smartHighlight, defaults.search.smartHighlight),
},
keyBindings: keyBindings,
};
Expand Down
Loading
Loading