diff --git a/lib/core/api/api_client.dart b/lib/core/api/api_client.dart index 0d556c7..aeda315 100644 --- a/lib/core/api/api_client.dart +++ b/lib/core/api/api_client.dart @@ -1,8 +1,7 @@ import 'package:dio/dio.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; - -import '../config/app_config.dart'; -import 'api_interceptors.dart'; +import 'package:tatlock_ui/core/api/api_interceptors.dart'; +import 'package:tatlock_ui/core/config/app_config.dart'; part 'api_client.g.dart'; diff --git a/lib/core/api/api_interceptors.dart b/lib/core/api/api_interceptors.dart index 135d292..808f0cb 100644 --- a/lib/core/api/api_interceptors.dart +++ b/lib/core/api/api_interceptors.dart @@ -2,10 +2,9 @@ import 'dart:developer' as developer; import 'package:dio/dio.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../auth/auth_provider.dart'; -import '../config/app_config.dart'; -import '../error/app_exception.dart'; +import 'package:tatlock_ui/core/auth/auth_provider.dart'; +import 'package:tatlock_ui/core/config/app_config.dart'; +import 'package:tatlock_ui/core/error/app_exception.dart'; /// Adds authentication token to requests. /// diff --git a/lib/features/control_room/containers/data/datasources/containers_datasource.dart b/lib/features/control_room/containers/data/datasources/containers_datasource.dart index 3327bb8..7b82f91 100644 --- a/lib/features/control_room/containers/data/datasources/containers_datasource.dart +++ b/lib/features/control_room/containers/data/datasources/containers_datasource.dart @@ -1,9 +1,8 @@ import 'package:dio/dio.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; - -import '../../../../../core/api/api_client.dart'; -import '../models/container_model.dart'; +import 'package:tatlock_ui/core/api/api_client.dart'; +import 'package:tatlock_ui/features/control_room/containers/data/models/container_model.dart'; part 'containers_datasource.g.dart'; diff --git a/lib/features/control_room/containers/data/models/container_model.dart b/lib/features/control_room/containers/data/models/container_model.dart index 647d07e..26ebc03 100644 --- a/lib/features/control_room/containers/data/models/container_model.dart +++ b/lib/features/control_room/containers/data/models/container_model.dart @@ -1,6 +1,5 @@ import 'package:freezed_annotation/freezed_annotation.dart'; - -import '../../domain/entities/container.dart'; +import 'package:tatlock_ui/features/control_room/containers/domain/entities/container.dart'; part 'container_model.freezed.dart'; part 'container_model.g.dart'; diff --git a/lib/features/control_room/containers/data/repositories/container_repository_impl.dart b/lib/features/control_room/containers/data/repositories/container_repository_impl.dart index 34a3b4c..da6ef36 100644 --- a/lib/features/control_room/containers/data/repositories/container_repository_impl.dart +++ b/lib/features/control_room/containers/data/repositories/container_repository_impl.dart @@ -1,8 +1,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; - -import '../../domain/entities/container.dart'; -import '../../domain/repositories/container_repository.dart'; -import '../datasources/containers_datasource.dart'; +import 'package:tatlock_ui/features/control_room/containers/data/datasources/containers_datasource.dart'; +import 'package:tatlock_ui/features/control_room/containers/domain/entities/container.dart'; +import 'package:tatlock_ui/features/control_room/containers/domain/repositories/container_repository.dart'; part 'container_repository_impl.g.dart'; diff --git a/lib/features/control_room/containers/domain/repositories/container_repository.dart b/lib/features/control_room/containers/domain/repositories/container_repository.dart index 1e553df..92da700 100644 --- a/lib/features/control_room/containers/domain/repositories/container_repository.dart +++ b/lib/features/control_room/containers/domain/repositories/container_repository.dart @@ -1,4 +1,4 @@ -import '../entities/container.dart'; +import 'package:tatlock_ui/features/control_room/containers/domain/entities/container.dart'; /// Repository interface for container operations. abstract class ContainerRepository { diff --git a/lib/features/control_room/containers/presentation/pages/containers_list_page.dart b/lib/features/control_room/containers/presentation/pages/containers_list_page.dart index c3d4cb3..67cf47f 100644 --- a/lib/features/control_room/containers/presentation/pages/containers_list_page.dart +++ b/lib/features/control_room/containers/presentation/pages/containers_list_page.dart @@ -1,10 +1,9 @@ import 'package:flutter/material.dart' hide Container; import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../../domain/entities/container.dart'; -import '../providers/containers_provider.dart'; -import '../widgets/container_logs_viewer.dart'; -import '../widgets/container_status_badge.dart'; +import 'package:tatlock_ui/features/control_room/containers/domain/entities/container.dart'; +import 'package:tatlock_ui/features/control_room/containers/presentation/providers/containers_provider.dart'; +import 'package:tatlock_ui/features/control_room/containers/presentation/widgets/container_logs_viewer.dart'; +import 'package:tatlock_ui/features/control_room/containers/presentation/widgets/container_status_badge.dart'; /// Page displaying the list of containers using DataGrid. class ContainersListPage extends ConsumerWidget { diff --git a/lib/features/control_room/containers/presentation/providers/containers_provider.dart b/lib/features/control_room/containers/presentation/providers/containers_provider.dart index 6098a16..9163f95 100644 --- a/lib/features/control_room/containers/presentation/providers/containers_provider.dart +++ b/lib/features/control_room/containers/presentation/providers/containers_provider.dart @@ -1,8 +1,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; - -import '../../../stacks/presentation/providers/stacks_provider.dart'; -import '../../data/repositories/container_repository_impl.dart'; -import '../../domain/entities/container.dart'; +import 'package:tatlock_ui/features/control_room/containers/data/repositories/container_repository_impl.dart'; +import 'package:tatlock_ui/features/control_room/containers/domain/entities/container.dart'; +import 'package:tatlock_ui/features/control_room/stacks/presentation/providers/stacks_provider.dart'; part 'containers_provider.g.dart'; diff --git a/lib/features/control_room/containers/presentation/widgets/container_logs_viewer.dart b/lib/features/control_room/containers/presentation/widgets/container_logs_viewer.dart index 5971092..80ce75d 100644 --- a/lib/features/control_room/containers/presentation/widgets/container_logs_viewer.dart +++ b/lib/features/control_room/containers/presentation/widgets/container_logs_viewer.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../providers/containers_provider.dart'; +import 'package:tatlock_ui/features/control_room/containers/presentation/providers/containers_provider.dart'; /// Widget for displaying container logs. class ContainerLogsViewer extends ConsumerStatefulWidget { diff --git a/lib/features/control_room/containers/presentation/widgets/container_status_badge.dart b/lib/features/control_room/containers/presentation/widgets/container_status_badge.dart index 9f48624..348781d 100644 --- a/lib/features/control_room/containers/presentation/widgets/container_status_badge.dart +++ b/lib/features/control_room/containers/presentation/widgets/container_status_badge.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart' hide Container; - -import '../../domain/entities/container.dart'; +import 'package:tatlock_ui/features/control_room/containers/domain/entities/container.dart'; /// Status badge for container state. class ContainerStatusBadge extends StatelessWidget { diff --git a/lib/features/control_room/stacks/data/datasources/stacks_datasource.dart b/lib/features/control_room/stacks/data/datasources/stacks_datasource.dart index 1bd8e52..6cdeb69 100644 --- a/lib/features/control_room/stacks/data/datasources/stacks_datasource.dart +++ b/lib/features/control_room/stacks/data/datasources/stacks_datasource.dart @@ -1,10 +1,9 @@ import 'package:dio/dio.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; - -import '../../../../../core/api/api_client.dart'; -import '../../../containers/data/datasources/containers_datasource.dart'; -import '../../../containers/domain/entities/container.dart'; -import '../../domain/entities/stack.dart'; +import 'package:tatlock_ui/core/api/api_client.dart'; +import 'package:tatlock_ui/features/control_room/containers/data/datasources/containers_datasource.dart'; +import 'package:tatlock_ui/features/control_room/containers/domain/entities/container.dart'; +import 'package:tatlock_ui/features/control_room/stacks/domain/entities/stack.dart'; part 'stacks_datasource.g.dart'; @@ -117,6 +116,11 @@ class StacksDatasource { Future deployStack(String stackId) async { await _dio.post('/infrastructure/stacks/$stackId/deploy'); } + + /// Rebuilds a stack (pulls fresh images and recreates containers). + Future rebuildStack(String stackId) async { + await _dio.post('/infrastructure/stacks/$stackId/rebuild'); + } } /// Provides the stacks datasource. diff --git a/lib/features/control_room/stacks/data/models/stack_model.dart b/lib/features/control_room/stacks/data/models/stack_model.dart index df81167..90b543d 100644 --- a/lib/features/control_room/stacks/data/models/stack_model.dart +++ b/lib/features/control_room/stacks/data/models/stack_model.dart @@ -1,6 +1,5 @@ import 'package:freezed_annotation/freezed_annotation.dart'; - -import '../../domain/entities/stack.dart'; +import 'package:tatlock_ui/features/control_room/stacks/domain/entities/stack.dart'; part 'stack_model.freezed.dart'; part 'stack_model.g.dart'; diff --git a/lib/features/control_room/stacks/data/repositories/stack_repository_impl.dart b/lib/features/control_room/stacks/data/repositories/stack_repository_impl.dart index b258eb0..d232c9a 100644 --- a/lib/features/control_room/stacks/data/repositories/stack_repository_impl.dart +++ b/lib/features/control_room/stacks/data/repositories/stack_repository_impl.dart @@ -1,8 +1,7 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; - -import '../../domain/entities/stack.dart'; -import '../../domain/repositories/stack_repository.dart'; -import '../datasources/stacks_datasource.dart'; +import 'package:tatlock_ui/features/control_room/stacks/data/datasources/stacks_datasource.dart'; +import 'package:tatlock_ui/features/control_room/stacks/domain/entities/stack.dart'; +import 'package:tatlock_ui/features/control_room/stacks/domain/repositories/stack_repository.dart'; part 'stack_repository_impl.g.dart'; @@ -67,6 +66,11 @@ class StackRepositoryImpl implements StackRepository { Future deployStack(String id) async { await _datasource.deployStack(id); } + + @override + Future rebuildStack(String id) async { + await _datasource.rebuildStack(id); + } } /// Provides the stack repository. diff --git a/lib/features/control_room/stacks/domain/repositories/stack_repository.dart b/lib/features/control_room/stacks/domain/repositories/stack_repository.dart index 47a51d0..bfaaff9 100644 --- a/lib/features/control_room/stacks/domain/repositories/stack_repository.dart +++ b/lib/features/control_room/stacks/domain/repositories/stack_repository.dart @@ -1,4 +1,4 @@ -import '../entities/stack.dart'; +import 'package:tatlock_ui/features/control_room/stacks/domain/entities/stack.dart'; /// Repository interface for stack operations. abstract class StackRepository { @@ -34,4 +34,7 @@ abstract class StackRepository { /// Deploys/redeploys a stack with current configuration. Future deployStack(String id); + + /// Rebuilds a stack (pulls fresh images and recreates containers). + Future rebuildStack(String id); } diff --git a/lib/features/control_room/stacks/presentation/pages/stack_detail_page.dart b/lib/features/control_room/stacks/presentation/pages/stack_detail_page.dart index b1e3023..d1eba71 100644 --- a/lib/features/control_room/stacks/presentation/pages/stack_detail_page.dart +++ b/lib/features/control_room/stacks/presentation/pages/stack_detail_page.dart @@ -1,13 +1,12 @@ import 'package:flutter/material.dart' hide Stack; import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../../../containers/presentation/providers/containers_provider.dart'; -import '../../../containers/presentation/widgets/container_logs_viewer.dart'; -import '../../../containers/presentation/widgets/container_status_badge.dart'; -import '../../domain/entities/stack.dart'; -import '../providers/stacks_provider.dart'; -import '../widgets/env_vars_editor.dart'; -import '../widgets/yaml_editor.dart'; +import 'package:tatlock_ui/features/control_room/containers/presentation/providers/containers_provider.dart'; +import 'package:tatlock_ui/features/control_room/containers/presentation/widgets/container_logs_viewer.dart'; +import 'package:tatlock_ui/features/control_room/containers/presentation/widgets/container_status_badge.dart'; +import 'package:tatlock_ui/features/control_room/stacks/domain/entities/stack.dart'; +import 'package:tatlock_ui/features/control_room/stacks/presentation/providers/stacks_provider.dart'; +import 'package:tatlock_ui/features/control_room/stacks/presentation/widgets/env_vars_editor.dart'; +import 'package:tatlock_ui/features/control_room/stacks/presentation/widgets/yaml_editor.dart'; /// Detail page for a selected stack with YAML editor, env vars, and container list. class StackDetailPage extends ConsumerStatefulWidget { @@ -164,6 +163,14 @@ class _StackEditor extends ConsumerWidget { ], ), ), + OutlinedButton.icon( + onPressed: () { + ref.read(stackConfigActionsProvider.notifier).rebuild(stack.id); + }, + icon: const Icon(Icons.refresh, size: 18), + label: const Text('Rebuild'), + ), + const SizedBox(width: 8), FilledButton.icon( onPressed: () { ref.read(stackConfigActionsProvider.notifier).deploy(stack.id); diff --git a/lib/features/control_room/stacks/presentation/providers/stacks_provider.dart b/lib/features/control_room/stacks/presentation/providers/stacks_provider.dart index 3401cdf..00d255f 100644 --- a/lib/features/control_room/stacks/presentation/providers/stacks_provider.dart +++ b/lib/features/control_room/stacks/presentation/providers/stacks_provider.dart @@ -1,7 +1,6 @@ import 'package:riverpod_annotation/riverpod_annotation.dart'; - -import '../../data/repositories/stack_repository_impl.dart'; -import '../../domain/entities/stack.dart'; +import 'package:tatlock_ui/features/control_room/stacks/data/repositories/stack_repository_impl.dart'; +import 'package:tatlock_ui/features/control_room/stacks/domain/entities/stack.dart'; part 'stacks_provider.g.dart'; @@ -80,4 +79,13 @@ class StackConfigActions extends _$StackConfigActions { ref.invalidate(stacksProvider); }); } + + Future rebuild(String stackId) async { + state = const AsyncValue.loading(); + state = await AsyncValue.guard(() async { + final repository = ref.read(stackRepositoryProvider); + await repository.rebuildStack(stackId); + ref.invalidate(stacksProvider); + }); + } } diff --git a/lib/features/control_room/stacks/presentation/widgets/env_vars_editor.dart b/lib/features/control_room/stacks/presentation/widgets/env_vars_editor.dart index 542ccf5..ca05006 100644 --- a/lib/features/control_room/stacks/presentation/widgets/env_vars_editor.dart +++ b/lib/features/control_room/stacks/presentation/widgets/env_vars_editor.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../providers/stacks_provider.dart'; +import 'package:tatlock_ui/features/control_room/stacks/presentation/providers/stacks_provider.dart'; /// Environment variables editor for stack configuration. class EnvVarsEditor extends ConsumerStatefulWidget { diff --git a/lib/features/control_room/stacks/presentation/widgets/stack_list_tile.dart b/lib/features/control_room/stacks/presentation/widgets/stack_list_tile.dart index 313cb50..31b124d 100644 --- a/lib/features/control_room/stacks/presentation/widgets/stack_list_tile.dart +++ b/lib/features/control_room/stacks/presentation/widgets/stack_list_tile.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart' hide Stack; - -import '../../domain/entities/stack.dart'; +import 'package:tatlock_ui/features/control_room/stacks/domain/entities/stack.dart'; /// List tile for displaying a stack in the sidebar. class StackListTile extends StatelessWidget { diff --git a/lib/features/control_room/stacks/presentation/widgets/yaml_editor.dart b/lib/features/control_room/stacks/presentation/widgets/yaml_editor.dart index 8baf1c3..57e1dd4 100644 --- a/lib/features/control_room/stacks/presentation/widgets/yaml_editor.dart +++ b/lib/features/control_room/stacks/presentation/widgets/yaml_editor.dart @@ -1,7 +1,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; - -import '../providers/stacks_provider.dart'; +import 'package:tatlock_ui/features/control_room/stacks/presentation/providers/stacks_provider.dart'; +import 'package:tatlock_ui/shared/components/code_editor/code_editor.dart'; /// YAML editor for stack compose configuration. class YamlEditor extends ConsumerStatefulWidget { @@ -17,20 +17,15 @@ class YamlEditor extends ConsumerStatefulWidget { } class _YamlEditorState extends ConsumerState { - final _controller = TextEditingController(); + final _editorKey = GlobalKey(); bool _hasChanges = false; String _originalYaml = ''; + bool _initialized = false; - @override - void dispose() { - _controller.dispose(); - super.dispose(); - } - - void _initializeController(String yaml) { - if (_originalYaml.isEmpty && yaml.isNotEmpty) { + void _initializeEditor(String yaml) { + if (!_initialized && yaml.isNotEmpty) { _originalYaml = yaml; - _controller.text = yaml; + _initialized = true; } } @@ -41,17 +36,18 @@ class _YamlEditorState extends ConsumerState { } void _onSave() { + final currentText = _editorKey.currentState?.text ?? ''; ref .read(stackConfigActionsProvider.notifier) - .saveYaml(widget.stackId, _controller.text); + .saveYaml(widget.stackId, currentText); setState(() { - _originalYaml = _controller.text; + _originalYaml = currentText; _hasChanges = false; }); } void _onReset() { - _controller.text = _originalYaml; + _editorKey.currentState?.text = _originalYaml; setState(() { _hasChanges = false; }); @@ -92,7 +88,7 @@ class _YamlEditorState extends ConsumerState { ), ), data: (yaml) { - _initializeController(yaml); + _initializeEditor(yaml); return Column( children: [ // Toolbar @@ -162,28 +158,13 @@ class _YamlEditorState extends ConsumerState { ], ), ), - // Editor + // Editor with syntax highlighting Expanded( - child: Container( - color: colorScheme.surfaceContainerLowest, - child: TextField( - controller: _controller, - onChanged: _onChanged, - maxLines: null, - expands: true, - textAlignVertical: TextAlignVertical.top, - style: TextStyle( - fontFamily: 'monospace', - fontSize: 13, - color: colorScheme.onSurface, - height: 1.5, - ), - decoration: const InputDecoration( - contentPadding: EdgeInsets.all(16), - border: InputBorder.none, - hintText: 'Enter docker-compose.yml content...', - ), - ), + child: CodeEditor( + key: _editorKey, + language: CodeEditorLanguage.yaml, + initialValue: yaml, + onChanged: _onChanged, ), ), ], diff --git a/lib/routing/app_router.dart b/lib/routing/app_router.dart index d14fc58..307901f 100644 --- a/lib/routing/app_router.dart +++ b/lib/routing/app_router.dart @@ -1,10 +1,9 @@ import 'package:flutter/material.dart'; import 'package:go_router/go_router.dart'; import 'package:riverpod_annotation/riverpod_annotation.dart'; - -import '../features/control_room/presentation/pages/control_room_page.dart'; -import '../features/front_hall/presentation/pages/front_hall_page.dart'; -import '../shared/layouts/app_scaffold.dart'; +import 'package:tatlock_ui/features/control_room/presentation/pages/control_room_page.dart'; +import 'package:tatlock_ui/features/front_hall/presentation/pages/front_hall_page.dart'; +import 'package:tatlock_ui/shared/layouts/app_scaffold.dart'; part 'app_router.g.dart'; diff --git a/lib/shared/components/code_editor/code_editor.dart b/lib/shared/components/code_editor/code_editor.dart new file mode 100644 index 0000000..84968bb --- /dev/null +++ b/lib/shared/components/code_editor/code_editor.dart @@ -0,0 +1,180 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_code_editor/flutter_code_editor.dart'; + +import 'code_editor_language.dart'; + +export 'code_editor_language.dart'; + +/// A generic code editor widget with syntax highlighting. +/// +/// Supports multiple languages via [CodeEditorLanguage] configuration. +class CodeEditor extends StatefulWidget { + const CodeEditor({ + super.key, + required this.language, + this.initialValue = '', + this.onChanged, + this.readOnly = false, + this.showLineNumbers = true, + }); + + /// The programming language for syntax highlighting. + final CodeEditorLanguage language; + + /// Initial content of the editor. + final String initialValue; + + /// Called when the content changes. + final ValueChanged? onChanged; + + /// Whether the editor is read-only. + final bool readOnly; + + /// Whether to show line numbers. + final bool showLineNumbers; + + @override + State createState() => CodeEditorState(); +} + +class CodeEditorState extends State { + late CodeController _controller; + + @override + void initState() { + super.initState(); + _initController(); + } + + void _initController() { + _controller = CodeController( + text: widget.initialValue, + language: widget.language.mode, + ); + + _controller.addListener(_onTextChanged); + } + + void _onTextChanged() { + widget.onChanged?.call(_controller.text); + } + + @override + void didUpdateWidget(CodeEditor oldWidget) { + super.didUpdateWidget(oldWidget); + if (oldWidget.language != widget.language) { + // Language changed, recreate controller + _controller.removeListener(_onTextChanged); + final currentText = _controller.text; + _controller.dispose(); + _controller = CodeController( + text: currentText, + language: widget.language.mode, + ); + _controller.addListener(_onTextChanged); + } + } + + @override + void dispose() { + _controller.removeListener(_onTextChanged); + _controller.dispose(); + super.dispose(); + } + + /// Gets the current text content. + String get text => _controller.text; + + /// Sets the text content programmatically. + set text(String value) { + _controller.text = value; + } + + @override + Widget build(BuildContext context) { + final colorScheme = Theme.of(context).colorScheme; + final isDark = Theme.of(context).brightness == Brightness.dark; + + return CodeTheme( + data: isDark ? _darkTheme(colorScheme) : _lightTheme(colorScheme), + child: CodeField( + controller: _controller, + readOnly: widget.readOnly, + gutterStyle: widget.showLineNumbers + ? GutterStyle( + showLineNumbers: true, + showErrors: false, + showFoldingHandles: false, + margin: 0, + textStyle: TextStyle( + fontFamily: 'monospace', + fontSize: 12, + color: colorScheme.outline, + ), + background: colorScheme.surfaceContainerLow, + ) + : GutterStyle.none, + textStyle: TextStyle( + fontFamily: 'monospace', + fontSize: 13, + height: 1.5, + color: colorScheme.onSurface, + ), + padding: const EdgeInsets.all(16), + background: colorScheme.surfaceContainerLowest, + ), + ); + } + + CodeThemeData _lightTheme(ColorScheme colorScheme) { + return CodeThemeData( + styles: { + 'root': TextStyle( + color: colorScheme.onSurface, + backgroundColor: colorScheme.surfaceContainerLowest, + ), + 'keyword': TextStyle(color: Colors.purple.shade700), + 'string': TextStyle(color: Colors.green.shade700), + 'number': TextStyle(color: Colors.blue.shade700), + 'comment': TextStyle( + color: Colors.grey.shade600, + fontStyle: FontStyle.italic, + ), + 'attr': TextStyle(color: Colors.orange.shade800), + 'literal': TextStyle(color: Colors.teal.shade700), + 'built_in': TextStyle(color: Colors.indigo.shade600), + 'type': TextStyle(color: Colors.cyan.shade800), + 'variable': TextStyle(color: Colors.brown.shade600), + 'symbol': TextStyle(color: Colors.red.shade700), + 'section': TextStyle(color: Colors.deepPurple.shade700), + 'meta': TextStyle(color: Colors.pink.shade700), + }, + ); + } + + CodeThemeData _darkTheme(ColorScheme colorScheme) { + return CodeThemeData( + styles: { + 'root': TextStyle( + color: colorScheme.onSurface, + backgroundColor: colorScheme.surfaceContainerLowest, + ), + 'keyword': const TextStyle(color: Color(0xFFC792EA)), + 'string': const TextStyle(color: Color(0xFFC3E88D)), + 'number': const TextStyle(color: Color(0xFFF78C6C)), + 'comment': TextStyle( + color: Colors.grey.shade500, + fontStyle: FontStyle.italic, + ), + 'attr': const TextStyle(color: Color(0xFFFFCB6B)), + 'literal': const TextStyle(color: Color(0xFF82AAFF)), + 'built_in': const TextStyle(color: Color(0xFF89DDFF)), + 'type': const TextStyle(color: Color(0xFFFFCB6B)), + 'variable': const TextStyle(color: Color(0xFFEEFFFF)), + 'symbol': const TextStyle(color: Color(0xFFF07178)), + 'section': const TextStyle(color: Color(0xFF82AAFF)), + 'meta': const TextStyle(color: Color(0xFFF78C6C)), + }, + ); + } +} diff --git a/lib/shared/components/code_editor/code_editor_language.dart b/lib/shared/components/code_editor/code_editor_language.dart new file mode 100644 index 0000000..1159135 --- /dev/null +++ b/lib/shared/components/code_editor/code_editor_language.dart @@ -0,0 +1,39 @@ +import 'package:highlight/highlight_core.dart'; +import 'package:highlight/languages/yaml.dart' as hl_yaml; +import 'package:highlight/languages/json.dart' as hl_json; +import 'package:highlight/languages/dockerfile.dart' as hl_dockerfile; +import 'package:highlight/languages/bash.dart' as hl_bash; +import 'package:highlight/languages/ini.dart' as hl_ini; + +/// Supported languages for the code editor. +enum CodeEditorLanguage { + yaml('yaml', 'YAML'), + json('json', 'JSON'), + dockerfile('dockerfile', 'Dockerfile'), + bash('bash', 'Bash'), + ini('ini', 'INI/ENV'), + plaintext('plaintext', 'Plain Text'); + + const CodeEditorLanguage(this.id, this.displayName); + + final String id; + final String displayName; + + /// Gets the highlight Mode for this language. + Mode? get mode { + switch (this) { + case CodeEditorLanguage.yaml: + return hl_yaml.yaml; + case CodeEditorLanguage.json: + return hl_json.json; + case CodeEditorLanguage.dockerfile: + return hl_dockerfile.dockerfile; + case CodeEditorLanguage.bash: + return hl_bash.bash; + case CodeEditorLanguage.ini: + return hl_ini.ini; + case CodeEditorLanguage.plaintext: + return null; + } + } +} diff --git a/lib/shared/components/data_grid/adapters/core_api_source.dart b/lib/shared/components/data_grid/adapters/core_api_source.dart index 6404d23..4858521 100644 --- a/lib/shared/components/data_grid/adapters/core_api_source.dart +++ b/lib/shared/components/data_grid/adapters/core_api_source.dart @@ -1,6 +1,5 @@ import 'package:dio/dio.dart'; - -import '../data_grid_source.dart'; +import 'package:tatlock_ui/shared/components/data_grid/data_grid_source.dart'; /// Data source adapter for Core API endpoints. /// diff --git a/lib/shared/components/data_grid/widgets/data_grid_actions_menu.dart b/lib/shared/components/data_grid/widgets/data_grid_actions_menu.dart index 16004c6..2ee8424 100644 --- a/lib/shared/components/data_grid/widgets/data_grid_actions_menu.dart +++ b/lib/shared/components/data_grid/widgets/data_grid_actions_menu.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; - -import '../data_grid_action.dart'; +import 'package:tatlock_ui/shared/components/data_grid/data_grid_action.dart'; /// Actions menu for a DataGrid row. class DataGridActionsMenu extends StatelessWidget { diff --git a/lib/shared/components/data_grid/widgets/data_grid_bulk_actions.dart b/lib/shared/components/data_grid/widgets/data_grid_bulk_actions.dart index 6e6d6e2..67d282b 100644 --- a/lib/shared/components/data_grid/widgets/data_grid_bulk_actions.dart +++ b/lib/shared/components/data_grid/widgets/data_grid_bulk_actions.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; - -import '../data_grid_action.dart'; +import 'package:tatlock_ui/shared/components/data_grid/data_grid_action.dart'; /// Bulk actions bar shown when items are selected. class DataGridBulkActions extends StatelessWidget { diff --git a/lib/shared/components/data_grid/widgets/data_grid_footer.dart b/lib/shared/components/data_grid/widgets/data_grid_footer.dart index 4050529..c9fd4fa 100644 --- a/lib/shared/components/data_grid/widgets/data_grid_footer.dart +++ b/lib/shared/components/data_grid/widgets/data_grid_footer.dart @@ -1,6 +1,5 @@ import 'package:flutter/material.dart'; - -import '../data_grid_config.dart'; +import 'package:tatlock_ui/shared/components/data_grid/data_grid_config.dart'; /// Footer for DataGrid with item count and pagination controls. class DataGridFooter extends StatelessWidget { diff --git a/lib/shared/components/data_grid/widgets/data_grid_header.dart b/lib/shared/components/data_grid/widgets/data_grid_header.dart index 2132a3f..cfdeb61 100644 --- a/lib/shared/components/data_grid/widgets/data_grid_header.dart +++ b/lib/shared/components/data_grid/widgets/data_grid_header.dart @@ -1,7 +1,6 @@ import 'package:flutter/material.dart'; - -import '../data_grid_column.dart'; -import '../data_grid_config.dart'; +import 'package:tatlock_ui/shared/components/data_grid/data_grid_column.dart'; +import 'package:tatlock_ui/shared/components/data_grid/data_grid_config.dart'; /// Header row for DataGrid. class DataGridHeader extends StatelessWidget { diff --git a/lib/shared/components/data_grid/widgets/data_grid_row.dart b/lib/shared/components/data_grid/widgets/data_grid_row.dart index 7e966c9..d10c3e1 100644 --- a/lib/shared/components/data_grid/widgets/data_grid_row.dart +++ b/lib/shared/components/data_grid/widgets/data_grid_row.dart @@ -1,8 +1,7 @@ import 'package:flutter/material.dart'; - -import '../data_grid_column.dart'; -import '../data_grid_config.dart'; -import 'data_grid_actions_menu.dart'; +import 'package:tatlock_ui/shared/components/data_grid/data_grid_column.dart'; +import 'package:tatlock_ui/shared/components/data_grid/data_grid_config.dart'; +import 'package:tatlock_ui/shared/components/data_grid/widgets/data_grid_actions_menu.dart'; /// A single data row in the DataGrid. class DataGridRow extends StatelessWidget {