Skip to content

Commit 6ff079e

Browse files
committed
feat: refactor sidebar component for collapsible functionality and improve styling
- Removed MonacoConfig interface as it is no longer needed. - Enhanced sidebar component to support collapsing with toggle button. - Updated sidebar styles for better responsiveness and visual appeal. - Implemented signal-based state management for sidebar collapse state. - Refactored time zones component to utilize signals for state management. - Added Dracula theme for CodeMirror editor with custom styling. - Introduced language extensions for CodeMirror to support various languages. - Created a diff editor component using CodeMirror for comparing code. - Developed a CodeMirror editor component with enhanced features and styling. - Implemented TypeScript formatting service using Prettier for code formatting. - Created a dedicated component for TypeScript formatting with UI integration.
1 parent 30f78c3 commit 6ff079e

38 files changed

Lines changed: 1256 additions & 1064 deletions

CLAUDE.md

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,4 +63,7 @@ Routes are centrally managed in `src/app/_services/route.service.ts` with:
6363
- Use `inject()` function for dependency injection
6464
- Prefer signals over observables for component state
6565
- Follow existing naming patterns: `f-json` (formatter), `c-json-yaml` (converter)
66-
- Abstract base classes for extensibility
66+
- Abstract base classes for extensibility
67+
- Use Angular control flow syntax (`@if`, `@for`, `@switch`) instead of structural directives (`*ngIf`, `*ngFor`, `*ngSwitch`)
68+
- Use `@for` syntax: `@for (item of items; track item.id) { ... }` instead of `*ngFor="let item of items; trackBy: trackByFn"`
69+
- Use signal-based inputs/outputs: `myInput = input()` and `myOutput = output()` instead of `@Input()` and `@Output()` decorators

package-lock.json

Lines changed: 325 additions & 6 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
},
1414
"private": true,
1515
"dependencies": {
16+
"@acrodata/code-editor": "^0.5.1",
1617
"@angular/animations": "^20.1.6",
1718
"@angular/common": "^20.1.6",
1819
"@angular/compiler": "^20.1.6",
@@ -23,11 +24,23 @@
2324
"@angular/platform-server": "^20.1.6",
2425
"@angular/router": "^20.1.6",
2526
"@angular/ssr": "^20.1.5",
27+
"@codemirror/commands": "^6.8.1",
28+
"@codemirror/lang-css": "^6.3.1",
29+
"@codemirror/lang-javascript": "^6.2.4",
30+
"@codemirror/lang-json": "^6.0.2",
31+
"@codemirror/lang-sql": "^6.9.1",
32+
"@codemirror/lang-xml": "^6.1.0",
33+
"@codemirror/lang-yaml": "^6.1.2",
34+
"@codemirror/language": "^6.11.2",
35+
"@codemirror/state": "^6.5.2",
36+
"@codemirror/view": "^6.38.1",
37+
"@lezer/highlight": "^1.2.1",
2638
"@ng-select/ng-select": "^20.1.0",
2739
"@stedi/prettier-plugin-jsonata": "^2.1.5",
40+
"codemirror": "^6.0.2",
2841
"express": "~4.21.2",
2942
"js-yaml": "^4.1.0",
30-
"monaco-editor": "^0.52.2",
43+
"prettier": "^3.6.2",
3144
"rxjs": "~7.8.2",
3245
"sql-formatter": "^15.6.6",
3346
"tslib": "^2.8.1",
@@ -60,4 +73,4 @@
6073
"typescript": "~5.8.3",
6174
"typescript-eslint": "^8.39.0"
6275
}
63-
}
76+
}

src/app/_services/route.service.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,14 @@ export class RouteService {
7272
loadComponent: () =>
7373
import('../formatters/f-javascript/f-javascript.component').then((mod) => mod.FJavascriptComponent),
7474
},
75+
{
76+
name: 'TypeScript',
77+
title: 'TypeScript Formatter',
78+
url: '/format/typescript',
79+
description: 'Format and beautify your TypeScript code with proper indentation and type definitions.',
80+
loadComponent: () =>
81+
import('../formatters/f-typescript/f-typescript.component').then((mod) => mod.FTypescriptComponent),
82+
},
7583
{
7684
name: 'SCSS',
7785
title: 'SCSS Formatter',
@@ -145,7 +153,8 @@ export class RouteService {
145153
name: 'File Diff',
146154
title: 'File Diff Checker',
147155
url: '/diff/files',
148-
description: 'Compare two files or text blocks and highlight the differences between them. Perfect for code reviews and content comparison.',
156+
description:
157+
'Compare two files or text blocks and highlight the differences between them. Perfect for code reviews and content comparison.',
149158
loadComponent: () => import('../diff/diff-view/diff-view.component').then((mod) => mod.DiffViewComponent),
150159
},
151160
],

src/app/app.component.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<app-animated-background></app-animated-background>
2-
<div class="left-gutter"></div>
2+
<div class="left-gutter" [ngClass]="{'collapsed': sidebarCollapsed()}"></div>
33
<div class="middle-section">
4-
<app-side-bar></app-side-bar>
4+
<app-side-bar (sidebarToggle)="onSidebarToggle($event)"></app-side-bar>
55
<div class="back">
66
<div class="route-container">
77

src/app/app.component.scss

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,13 @@ app-side-bar {
3636
// rgba(15, 15, 26, 0.95) 0%,
3737
// rgba(26, 26, 46, 0.9) 50%,
3838
// rgba(22, 33, 62, 0.95) 100%);
39-
width: 280px;
39+
width: 240px;
4040
flex-shrink: 0;
41+
transition: width 0.3s ease;
42+
}
43+
44+
.left-gutter.collapsed {
45+
width: 64px;
4146
}
4247

4348

@@ -93,7 +98,7 @@ app-side-bar {
9398
display: flex;
9499
flex-direction: column;
95100
flex-grow: 1;
96-
padding: 1.5rem;
101+
padding: 1rem 1.25rem;
97102
position: relative;
98103
z-index: 2;
99104
}

src/app/app.component.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,21 @@
1-
import { Component, inject } from '@angular/core';
1+
import { Component, inject, signal } from '@angular/core';
22
import { Router, RouterOutlet } from '@angular/router';
3+
import { CommonModule } from '@angular/common';
34
import { RouteService } from './_services/route.service';
45
import { SideBarComponent } from './nav/side-bar/side-bar.component';
56
import { AnimatedBackgroundComponent } from './components/animated-background/animated-background.component';
67
@Component({
78
selector: 'app-root',
89
templateUrl: './app.component.html',
910
styleUrls: ['./app.component.scss'],
10-
imports: [RouterOutlet, SideBarComponent, AnimatedBackgroundComponent],
11+
imports: [CommonModule, RouterOutlet, SideBarComponent, AnimatedBackgroundComponent],
1112
})
1213
export class AppComponent {
1314
router = inject(Router);
1415
RouterService = RouteService;
16+
sidebarCollapsed = signal(false);
17+
18+
onSidebarToggle(collapsed: boolean) {
19+
this.sidebarCollapsed.set(collapsed);
20+
}
1521
}
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import { EditorView } from '@codemirror/view';
2+
import { Extension } from '@codemirror/state';
3+
import { HighlightStyle, syntaxHighlighting } from '@codemirror/language';
4+
import { tags as t } from '@lezer/highlight';
5+
6+
const draculaColors = {
7+
background: '#1a1a2e',
8+
foreground: '#f1f5f9',
9+
selection: '#3d4852',
10+
lineHighlight: '#252b3a',
11+
cursor: '#50fa7b',
12+
comment: '#8be9fd',
13+
string: '#50fa7b',
14+
number: '#bd93f9',
15+
keyword: '#8be9fd',
16+
function: '#8be9fd',
17+
className: '#50fa7b',
18+
tag: '#8be9fd',
19+
attribute: '#50fa7b',
20+
property: '#8be9fd',
21+
variable: '#8be9fd',
22+
constant: '#bd93f9',
23+
type: '#bd93f9',
24+
operator: '#ff79c6',
25+
punctuation: '#f8f8f2',
26+
bracket: '#f8f8f2',
27+
invalid: '#ff5555',
28+
deprecated: '#ffb86c',
29+
lineNumber: '#64748b',
30+
activeLineNumber: '#8be9fd',
31+
gutter: '#16213e',
32+
scrollbar: '#50fa7b30',
33+
scrollbarHover: '#50fa7b50',
34+
scrollbarActive: '#50fa7b70',
35+
};
36+
37+
const draculaTheme = EditorView.theme({
38+
'&': {
39+
color: draculaColors.foreground,
40+
backgroundColor: draculaColors.background,
41+
},
42+
'.cm-content': {
43+
padding: '16px',
44+
caretColor: draculaColors.cursor,
45+
},
46+
'.cm-focused .cm-cursor': {
47+
borderLeftColor: draculaColors.cursor,
48+
},
49+
'.cm-focused .cm-selectionBackground, ::selection': {
50+
backgroundColor: draculaColors.selection,
51+
},
52+
'.cm-selectionBackground': {
53+
backgroundColor: `${draculaColors.selection}99`,
54+
},
55+
'.cm-activeLine': {
56+
backgroundColor: draculaColors.lineHighlight,
57+
},
58+
'.cm-gutters': {
59+
backgroundColor: draculaColors.gutter,
60+
color: draculaColors.lineNumber,
61+
borderRight: 'none',
62+
},
63+
'.cm-activeLineGutter': {
64+
backgroundColor: draculaColors.lineHighlight,
65+
color: draculaColors.activeLineNumber,
66+
},
67+
'.cm-lineNumbers': {
68+
color: draculaColors.lineNumber,
69+
},
70+
'.cm-foldPlaceholder': {
71+
backgroundColor: 'transparent',
72+
border: 'none',
73+
color: draculaColors.comment,
74+
},
75+
'.cm-tooltip': {
76+
border: 'none',
77+
backgroundColor: draculaColors.background,
78+
color: draculaColors.foreground,
79+
},
80+
'.cm-tooltip-autocomplete': {
81+
'& > ul > li[aria-selected]': {
82+
backgroundColor: draculaColors.selection,
83+
color: draculaColors.foreground,
84+
}
85+
},
86+
'.cm-searchMatch': {
87+
backgroundColor: '#50fa7b40',
88+
outline: '1px solid #50fa7b',
89+
},
90+
'.cm-searchMatch.cm-searchMatch-selected': {
91+
backgroundColor: '#50fa7b80',
92+
},
93+
'.cm-panels': {
94+
backgroundColor: draculaColors.gutter,
95+
color: draculaColors.foreground,
96+
},
97+
'.cm-panels.cm-panels-top': {
98+
borderBottom: '2px solid #374151',
99+
},
100+
'.cm-panels.cm-panels-bottom': {
101+
borderTop: '2px solid #374151',
102+
},
103+
'.cm-scroller': {
104+
fontFamily: 'Monaco, Menlo, "Ubuntu Mono", monospace',
105+
fontSize: '14px',
106+
lineHeight: '1.5',
107+
},
108+
}, { dark: true });
109+
110+
const draculaHighlightStyle = HighlightStyle.define([
111+
{ tag: t.comment, color: draculaColors.comment, fontStyle: 'italic' },
112+
{ tag: t.lineComment, color: draculaColors.comment, fontStyle: 'italic' },
113+
{ tag: t.blockComment, color: draculaColors.comment, fontStyle: 'italic' },
114+
{ tag: t.docComment, color: draculaColors.comment, fontStyle: 'italic' },
115+
116+
{ tag: t.keyword, color: draculaColors.keyword, fontWeight: 'bold' },
117+
{ tag: t.controlKeyword, color: draculaColors.keyword, fontWeight: 'bold' },
118+
{ tag: t.modifier, color: draculaColors.keyword, fontWeight: 'bold' },
119+
{ tag: t.operatorKeyword, color: draculaColors.keyword, fontWeight: 'bold' },
120+
121+
{ tag: t.variableName, color: draculaColors.variable },
122+
{ tag: t.typeName, color: draculaColors.type, fontStyle: 'italic' },
123+
{ tag: t.atom, color: draculaColors.constant },
124+
{ tag: t.bool, color: draculaColors.constant },
125+
{ tag: t.special(t.variableName), color: draculaColors.constant },
126+
127+
{ tag: t.number, color: draculaColors.number },
128+
{ tag: t.string, color: draculaColors.string },
129+
{ tag: t.special(t.string), color: draculaColors.string },
130+
{ tag: t.regexp, color: draculaColors.string },
131+
132+
{ tag: t.definition(t.typeName), color: draculaColors.className, fontWeight: 'bold' },
133+
{ tag: t.className, color: draculaColors.className, fontWeight: 'bold' },
134+
{ tag: t.definition(t.propertyName), color: draculaColors.function, fontWeight: 'bold' },
135+
{ tag: t.function(t.variableName), color: draculaColors.function, fontWeight: 'bold' },
136+
{ tag: t.function(t.propertyName), color: draculaColors.function, fontWeight: 'bold' },
137+
138+
{ tag: t.tagName, color: draculaColors.tag },
139+
{ tag: t.attributeName, color: draculaColors.attribute },
140+
{ tag: t.attributeValue, color: draculaColors.string },
141+
142+
{ tag: t.propertyName, color: draculaColors.property },
143+
{ tag: t.namespace, color: draculaColors.variable },
144+
{ tag: t.macroName, color: draculaColors.function },
145+
146+
{ tag: t.operator, color: draculaColors.operator },
147+
{ tag: t.separator, color: draculaColors.punctuation },
148+
{ tag: t.punctuation, color: draculaColors.punctuation },
149+
{ tag: t.bracket, color: draculaColors.bracket },
150+
{ tag: t.angleBracket, color: draculaColors.bracket },
151+
{ tag: t.squareBracket, color: draculaColors.bracket },
152+
{ tag: t.paren, color: draculaColors.bracket },
153+
{ tag: t.brace, color: draculaColors.bracket },
154+
155+
{ tag: t.invalid, color: draculaColors.invalid, fontWeight: 'bold' },
156+
{ tag: t.deleted, color: draculaColors.invalid, backgroundColor: '#ff555540' },
157+
{ tag: t.inserted, color: draculaColors.string, backgroundColor: '#50fa7b40' },
158+
{ tag: t.changed, color: draculaColors.deprecated, backgroundColor: '#ffb86c40' },
159+
160+
{ tag: t.strong, fontWeight: 'bold' },
161+
{ tag: t.emphasis, fontStyle: 'italic' },
162+
{ tag: t.strikethrough, textDecoration: 'line-through' },
163+
{ tag: t.link, color: draculaColors.string, textDecoration: 'underline' },
164+
{ tag: t.heading, color: draculaColors.function, fontWeight: 'bold' },
165+
]);
166+
167+
export const draculaExtension: Extension = [
168+
draculaTheme,
169+
syntaxHighlighting(draculaHighlightStyle),
170+
];
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { Extension } from '@codemirror/state';
2+
import { javascript } from '@codemirror/lang-javascript';
3+
import { json } from '@codemirror/lang-json';
4+
import { sql } from '@codemirror/lang-sql';
5+
import { css } from '@codemirror/lang-css';
6+
import { yaml } from '@codemirror/lang-yaml';
7+
import { xml } from '@codemirror/lang-xml';
8+
9+
export const getLanguageExtension = (language: string): Extension[] => {
10+
switch (language.toLowerCase()) {
11+
case 'javascript':
12+
case 'js':
13+
return [javascript({ jsx: false, typescript: false })];
14+
case 'typescript':
15+
case 'ts':
16+
return [javascript({ jsx: false, typescript: true })];
17+
case 'json':
18+
case 'jsonc':
19+
return [json()];
20+
case 'sql':
21+
return [sql()];
22+
case 'css':
23+
case 'scss':
24+
case 'sass':
25+
case 'less':
26+
return [css()];
27+
case 'yaml':
28+
case 'yml':
29+
return [yaml()];
30+
case 'xml':
31+
case 'html':
32+
case 'htm':
33+
return [xml()];
34+
default:
35+
return [];
36+
}
37+
};

0 commit comments

Comments
 (0)