Continue roadmap: local TLS and agent review preparation

This commit is contained in:
Hamza Ayed
2026-10-04 00:35:05 +03:00
parent 379098f3d6
commit d29275043a
28 changed files with 3226 additions and 47 deletions
@@ -104,9 +104,13 @@ class _ChatPageState extends State<ChatPage> {
context.read<ChatCubit>().toggleWorkspaceMode();
return;
}
await _addWorkspaceProject();
}
Future<void> _addWorkspaceProject() async {
try {
final directory = await getDirectoryPath(
confirmButtonText: 'اختيار مساحة العمل',
confirmButtonText: 'تسجيل هذا المشروع',
);
if (!mounted || directory == null) return;
final cubit = context.read<ChatCubit>();
@@ -116,13 +120,13 @@ class _ChatPageState extends State<ChatPage> {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'تعذر اختيار مساحة العمل: ${cubit.state.error ?? 'تعذر الاتصال بالخادم المحلي.'}',
'تعذر تسجيل المشروع: ${cubit.state.error ?? 'تعذر الاتصال بالخادم المحلي.'}',
),
),
);
return;
}
final name = directory.split(RegExp(r'[/\\]')).last;
final name = _workspaceName(directory);
if (files.isNotEmpty) {
final selectedFiles = await _showWorkspaceFiles(files);
if (!mounted) return;
@@ -132,19 +136,68 @@ class _ChatPageState extends State<ChatPage> {
SnackBar(
content: Text(
files.isEmpty
? 'تم اختيار مساحة العمل: $name. لم أجد ملفات نصية مدعومة.'
: 'تم اختيار مساحة العمل: $name',
? 'تم تسجيل المشروع: $name. لم أجد ملفات نصية مدعومة.'
: 'تم تسجيل المشروع وفتحه: $name',
),
),
);
} catch (error) {
if (!mounted) return;
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('تعذر اختيار مجلد مساحة العمل: $error')),
SnackBar(content: Text('تعذر تسجيل مجلد المشروع: $error')),
);
}
}
String _workspaceName(String path) {
final parts = path.split(RegExp(r'[/\\]')).where((part) => part.isNotEmpty);
return parts.isEmpty ? path : parts.last;
}
Future<void> _openSavedWorkspace(String path) async {
final cubit = context.read<ChatCubit>();
await cubit.selectWorkspace(path);
if (!mounted) return;
if (cubit.state.selectedWorkspacePath != path) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(
'تعذر فتح المشروع: ${cubit.state.error ?? 'المجلد غير متاح.'}',
),
),
);
return;
}
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('تم فتح المشروع: ${_workspaceName(path)}')),
);
}
Future<void> _removeSavedWorkspace(String path) async {
final remove = await showDialog<bool>(
context: context,
builder:
(dialogContext) => AlertDialog(
title: const Text('إزالة المشروع من القائمة؟'),
content: Text(
'سيُزال ${_workspaceName(path)} من قائمة المشاريع المحفوظة فقط. لن تُحذف ملفاته.',
),
actions: [
TextButton(
onPressed: () => Navigator.pop(dialogContext, false),
child: const Text('إلغاء'),
),
FilledButton(
onPressed: () => Navigator.pop(dialogContext, true),
child: const Text('إزالة'),
),
],
),
);
if (!mounted || remove != true) return;
await context.read<ChatCubit>().removeSavedWorkspace(path);
}
Future<void> _indexSelectedWorkspaceKnowledge() async {
final cubit = context.read<ChatCubit>();
try {
@@ -1026,15 +1079,92 @@ class _ChatPageState extends State<ChatPage> {
),
),
const SizedBox(height: 24),
Row(
children: [
const Expanded(
child: Text(
'مشاريعي',
style: TextStyle(
color: Color(0xFF718078),
fontSize: 12,
fontWeight: FontWeight.w600,
),
),
),
IconButton(
tooltip: 'تسجيل مشروع جديد',
visualDensity: VisualDensity.compact,
onPressed: chat.isSending ? null : _addWorkspaceProject,
icon: const Icon(Icons.create_new_folder_outlined, size: 19),
),
],
),
if (chat.savedWorkspacePaths.isEmpty)
TextButton.icon(
onPressed: chat.isSending ? null : _addWorkspaceProject,
icon: const Icon(Icons.add, size: 16),
label: const Text('تسجيل مشروع جديد'),
style: TextButton.styleFrom(
alignment: Alignment.centerRight,
padding: const EdgeInsets.symmetric(horizontal: 4, vertical: 6),
),
)
else
ConstrainedBox(
constraints: const BoxConstraints(maxHeight: 148),
child: ListView.builder(
shrinkWrap: true,
itemCount: chat.savedWorkspacePaths.length,
itemBuilder: (context, index) {
final path = chat.savedWorkspacePaths[index];
final selected = chat.selectedWorkspacePath == path;
return Padding(
padding: const EdgeInsets.only(bottom: 3),
child: Material(
color: selected
? const Color(0xFFDCEAE3)
: Colors.transparent,
borderRadius: BorderRadius.circular(10),
child: ListTile(
dense: true,
visualDensity: VisualDensity.compact,
contentPadding: const EdgeInsets.only(left: 3, right: 8),
leading: Icon(
Icons.folder_open_outlined,
size: 18,
color: selected ? _green : const Color(0xFF68766E),
),
title: Text(
_workspaceName(path),
maxLines: 1,
overflow: TextOverflow.ellipsis,
style: const TextStyle(fontSize: 12, color: _ink),
),
trailing: IconButton(
tooltip: 'إزالة المشروع من القائمة',
visualDensity: VisualDensity.compact,
icon: const Icon(Icons.close, size: 16),
onPressed: () => _removeSavedWorkspace(path),
),
onTap: chat.isSending
? null
: () => _openSavedWorkspace(path),
),
),
);
},
),
),
const SizedBox(height: 12),
const Text(
'مساحة العمل',
'محادثاتي',
style: TextStyle(
color: Color(0xFF718078),
fontSize: 12,
fontWeight: FontWeight.w600,
),
),
const SizedBox(height: 12),
const SizedBox(height: 8),
if (chat.isLoadingHistory)
const Padding(
padding: EdgeInsets.symmetric(vertical: 18),