Compare commits

...

1 Commits
main ... ai-dev

Author SHA1 Message Date
Flatlogic Bot
3c2be4b978 Autosave: 20260516-113047 2026-05-16 11:30:46 +00:00
9 changed files with 1503 additions and 89 deletions

View File

@ -1785,5 +1785,707 @@
"createdAt": "T08:15:12.895Z",
"updatedAt": "T08:15:12.915Z",
"stage": "intro"
},
{
"id": "sess_vsyycl02mp83hq7p",
"title": "سثث",
"scope": "",
"actors": [],
"useCases": [],
"inputOutputs": [],
"functionPoints": [],
"planning": {
"developers": 3,
"sprintWeeks": 2,
"hoursPerDay": 8
},
"history": [
{
"role": "assistant",
"payload": {
"reply": "بدأنا مشروعاً جديداً بعنوان \"سثث\". هل تود أن تبدأ برفع ملف SRS، أم تصف لي الهدف العام للمشروع مباشرة؟",
"stage": "intro",
"scope": "",
"actors": [],
"functionPoints": [],
"useCases": [],
"inputOutputs": [],
"isComplete": false
}
}
],
"srsDraft": null,
"needsStructureConfirmation": false,
"isComplete": false,
"createdAt": "T08:39:23.893Z",
"updatedAt": "T08:39:23.894Z",
"stage": "intro"
},
{
"id": "sess_u7o8c5d0mp83wmtt",
"title": "فحص واجهة الشات",
"scope": "نظام لفحص واجهة الشات يسمح للمستخدمين بتسجيل الدخول، للمديرين بمراجعة الطلبات، وللعملاء بإرسال طلبات الخدمة، مع إصدار النظام لتقارير شهرية.",
"actors": [
"المستخدم",
"المدير",
"العميل",
"النظام"
],
"useCases": [
{
"id": "UC-01",
"title": "تسجيل الدخول",
"actor": "المستخدم",
"preconditions": "يجب أن يكون لدى المستخدم حساب صالح.",
"mainFlow": [
"يقوم المستخدم بإدخال اسم المستخدم وكلمة المرور.",
"يقوم النظام بالتحقق من صحة بيانات الاعتماد.",
"إذا كانت البيانات صحيحة، يتم تسجيل دخول المستخدم بنجاح.",
"إذا كانت البيانات غير صحيحة، يتم عرض رسالة خطأ."
],
"alternateFlow": [
"في حالة نسيان كلمة المرور، يمكن للمستخدم طلب إعادة تعيينها."
]
},
{
"id": "UC-02",
"title": "مراجعة الطلبات",
"actor": "المدير",
"preconditions": "يجب أن يكون لدى المدير صلاحيات الوصول إلى قسم مراجعة الطلبات.",
"mainFlow": [
"يقوم المدير بالوصول إلى قسم مراجعة الطلبات.",
"يعرض النظام قائمة بالطلبات المقدمة.",
"يمكن للمدير اختيار طلب معين لعرض تفاصيله.",
"يمكن للمدير تحديث حالة الطلب (مثل: قيد المعالجة، مكتمل، مرفوض)."
],
"alternateFlow": []
},
{
"id": "UC-03",
"title": "إصدار تقرير شهري",
"actor": "النظام",
"preconditions": "يجب أن يكون النظام قادراً على الوصول إلى بيانات الطلبات.",
"mainFlow": [
"في نهاية كل شهر، يقوم النظام بتشغيل عملية إنشاء التقرير.",
"يقوم النظام بتجميع بيانات الطلبات خلال الشهر.",
"يقوم النظام بإنشاء تقرير شهري يتضمن ملخصاً للطلبات وحالاتها."
],
"alternateFlow": [
"يمكن للمدير طلب إنشاء تقرير في أي وقت."
]
},
{
"id": "UC-04",
"title": "إرسال طلب خدمة",
"actor": "العميل",
"preconditions": "يجب أن يكون لدى العميل حساب أو القدرة على تقديم طلب كضيف.",
"mainFlow": [
"يقوم العميل بالوصول إلى واجهة تقديم طلب الخدمة.",
"يقوم العميل بإدخال تفاصيل الطلب (مثل: وصف الخدمة المطلوبة، معلومات الاتصال).",
"يقوم العميل بإرسال الطلب.",
"يقوم النظام بتسجيل الطلب وتعيين معرف فريد له.",
"يعرض النظام رسالة تأكيد للعميل."
],
"alternateFlow": []
}
],
"inputOutputs": [
{
"id": "IO-01",
"type": "input",
"name": "بيانات تسجيل الدخول (اسم المستخدم، كلمة المرور)",
"source": "المستخدم",
"destination": "النظام",
"description": "المعلومات التي يدخلها المستخدم لتسجيل الدخول."
},
{
"id": "IO-02",
"type": "output",
"name": "تأكيد تسجيل الدخول / رسالة خطأ",
"source": "النظام",
"destination": "المستخدم",
"description": "نتيجة عملية تسجيل الدخول."
},
{
"id": "IO-03",
"type": "input",
"name": "معرف الطلب / تحديث الحالة",
"source": "المدير",
"destination": "النظام",
"description": "المعلومات التي يدخلها المدير لتحديث حالة الطلب أو مراجعته."
},
{
"id": "IO-04",
"type": "output",
"name": "قائمة الطلبات / تفاصيل الطلب",
"source": "النظام",
"destination": "المدير",
"description": "البيانات التي يعرضها النظام للمدير لمراجعة الطلبات."
},
{
"id": "IO-05",
"type": "output",
"name": "التقرير الشهري",
"source": "النظام",
"destination": "المدير",
"description": "التقرير الذي يولده النظام شهرياً."
},
{
"id": "IO-06",
"type": "input",
"name": "تفاصيل طلب الخدمة (وصف، معلومات الاتصال)",
"source": "العميل",
"destination": "النظام",
"description": "المعلومات التي يقدمها العميل لإنشاء طلب خدمة."
},
{
"id": "IO-07",
"type": "output",
"name": "تأكيد تقديم الطلب",
"source": "النظام",
"destination": "العميل",
"description": "رسالة تأكيد للعميل بعد تقديم طلب الخدمة."
}
],
"functionPoints": [
{
"id": "FP-01",
"name": "تسجيل الدخول",
"description": "تغطي حالة الاستخدام \"تسجيل الدخول\" للفاعل المستخدم.",
"complexity": "معقدة",
"fpScore": 6
},
{
"id": "FP-02",
"name": "مراجعة الطلبات",
"description": "تغطي حالة الاستخدام \"مراجعة الطلبات\" للفاعل المدير.",
"complexity": "متوسطة",
"fpScore": 4
},
{
"id": "FP-03",
"name": "إصدار تقرير شهري",
"description": "تغطي حالة الاستخدام \"إصدار تقرير شهري\" للفاعل النظام.",
"complexity": "معقدة",
"fpScore": 6
},
{
"id": "FP-04",
"name": "إرسال طلب خدمة",
"description": "تغطي حالة الاستخدام \"إرسال طلب خدمة\" للفاعل العميل.",
"complexity": "متوسطة",
"fpScore": 4
}
],
"planning": {
"developers": 3,
"sprintWeeks": 2,
"hoursPerDay": 8
},
"history": [
{
"role": "assistant",
"payload": {
"reply": "بدأنا مشروعاً جديداً بعنوان \"فحص واجهة الشات\". هل تود أن تبدأ برفع ملف SRS، أم تصف لي الهدف العام للمشروع مباشرة؟",
"stage": "intro",
"scope": "",
"actors": [],
"functionPoints": [],
"useCases": [],
"inputOutputs": [],
"isComplete": false
}
},
{
"role": "assistant",
"payload": {
"reply": "تم تحليل النص المرفوع. اقترحت 4 Actors و4 Use Cases و3 Inputs و4 Outputs. هل تفاصيل حالات الاستخدام والمدخلات والمخرجات والوظائف المستخرجة تعكس بدقة متطلبات النظام المذكورة في نص SRS؟",
"stage": "exploration",
"scope": "نظام لفحص واجهة الشات يسمح للمستخدمين بتسجيل الدخول، للمديرين بمراجعة الطلبات، وللعملاء بإرسال طلبات الخدمة، مع إصدار النظام لتقارير شهرية.",
"actors": [
"المستخدم",
"المدير",
"العميل",
"النظام"
],
"functionPoints": [
{
"id": "FP-01",
"name": "تسجيل الدخول",
"description": "وظيفة تسجيل دخول المستخدمين إلى النظام.",
"complexity": "متوسطة",
"fpScore": 5
},
{
"id": "FP-02",
"name": "إدارة الطلبات",
"description": "وظيفة مراجعة وتحديث حالات الطلبات من قبل المدير.",
"complexity": "معقدة",
"fpScore": 7
},
{
"id": "FP-03",
"name": "إنشاء تقرير شهري",
"description": "وظيفة توليد تقرير شهري آلياً بواسطة النظام.",
"complexity": "معقدة",
"fpScore": 6
},
{
"id": "FP-04",
"name": "تقديم طلب خدمة",
"description": "وظيفة تمكين العملاء من إرسال طلبات الخدمة.",
"complexity": "متوسطة",
"fpScore": 5
}
],
"useCases": [
{
"id": "UC-01",
"title": "تسجيل الدخول",
"actor": "المستخدم",
"preconditions": "يجب أن يكون لدى المستخدم حساب صالح.",
"mainFlow": [
"يقوم المستخدم بإدخال اسم المستخدم وكلمة المرور.",
"يقوم النظام بالتحقق من صحة بيانات الاعتماد.",
"إذا كانت البيانات صحيحة، يتم تسجيل دخول المستخدم بنجاح.",
"إذا كانت البيانات غير صحيحة، يتم عرض رسالة خطأ."
],
"alternateFlow": [
"في حالة نسيان كلمة المرور، يمكن للمستخدم طلب إعادة تعيينها."
]
},
{
"id": "UC-02",
"title": "مراجعة الطلبات",
"actor": "المدير",
"preconditions": "يجب أن يكون لدى المدير صلاحيات الوصول إلى قسم مراجعة الطلبات.",
"mainFlow": [
"يقوم المدير بالوصول إلى قسم مراجعة الطلبات.",
"يعرض النظام قائمة بالطلبات المقدمة.",
"يمكن للمدير اختيار طلب معين لعرض تفاصيله.",
"يمكن للمدير تحديث حالة الطلب (مثل: قيد المعالجة، مكتمل، مرفوض)."
],
"alternateFlow": []
},
{
"id": "UC-03",
"title": "إصدار تقرير شهري",
"actor": "النظام",
"preconditions": "يجب أن يكون النظام قادراً على الوصول إلى بيانات الطلبات.",
"mainFlow": [
"في نهاية كل شهر، يقوم النظام بتشغيل عملية إنشاء التقرير.",
"يقوم النظام بتجميع بيانات الطلبات خلال الشهر.",
"يقوم النظام بإنشاء تقرير شهري يتضمن ملخصاً للطلبات وحالاتها."
],
"alternateFlow": [
"يمكن للمدير طلب إنشاء تقرير في أي وقت."
]
},
{
"id": "UC-04",
"title": "إرسال طلب خدمة",
"actor": "العميل",
"preconditions": "يجب أن يكون لدى العميل حساب أو القدرة على تقديم طلب كضيف.",
"mainFlow": [
"يقوم العميل بالوصول إلى واجهة تقديم طلب الخدمة.",
"يقوم العميل بإدخال تفاصيل الطلب (مثل: وصف الخدمة المطلوبة، معلومات الاتصال).",
"يقوم العميل بإرسال الطلب.",
"يقوم النظام بتسجيل الطلب وتعيين معرف فريد له.",
"يعرض النظام رسالة تأكيد للعميل."
],
"alternateFlow": []
}
],
"inputOutputs": [
{
"id": "IO-01",
"type": "input",
"name": "بيانات تسجيل الدخول (اسم المستخدم، كلمة المرور)",
"source": "المستخدم",
"destination": "النظام",
"description": "المعلومات التي يدخلها المستخدم لتسجيل الدخول."
},
{
"id": "IO-02",
"type": "output",
"name": "تأكيد تسجيل الدخول / رسالة خطأ",
"source": "النظام",
"destination": "المستخدم",
"description": "نتيجة عملية تسجيل الدخول."
},
{
"id": "IO-03",
"type": "input",
"name": "معرف الطلب / تحديث الحالة",
"source": "المدير",
"destination": "النظام",
"description": "المعلومات التي يدخلها المدير لتحديث حالة الطلب أو مراجعته."
},
{
"id": "IO-04",
"type": "output",
"name": "قائمة الطلبات / تفاصيل الطلب",
"source": "النظام",
"destination": "المدير",
"description": "البيانات التي يعرضها النظام للمدير لمراجعة الطلبات."
},
{
"id": "IO-05",
"type": "output",
"name": "التقرير الشهري",
"source": "النظام",
"destination": "المدير",
"description": "التقرير الذي يولده النظام شهرياً."
},
{
"id": "IO-06",
"type": "input",
"name": "تفاصيل طلب الخدمة (وصف، معلومات الاتصال)",
"source": "العميل",
"destination": "النظام",
"description": "المعلومات التي يقدمها العميل لإنشاء طلب خدمة."
},
{
"id": "IO-07",
"type": "output",
"name": "تأكيد تقديم الطلب",
"source": "النظام",
"destination": "العميل",
"description": "رسالة تأكيد للعميل بعد تقديم طلب الخدمة."
}
],
"isComplete": false
}
},
{
"role": "assistant",
"payload": {
"reply": "تم اعتماد الهيكل الأولي: 4 Actors، 4 Use Cases، و7 Inputs/Outputs. أصبح لدينا أساس جيد للتقدير؛ هل تريد تثبيت التقدير الحالي وتوليد التقرير أم إضافة تفاصيل تقنية أخرى؟",
"stage": "evaluation",
"scope": "نظام لفحص واجهة الشات يسمح للمستخدمين بتسجيل الدخول، للمديرين بمراجعة الطلبات، وللعملاء بإرسال طلبات الخدمة، مع إصدار النظام لتقارير شهرية.",
"actors": [
"المستخدم",
"المدير",
"العميل",
"النظام"
],
"functionPoints": [
{
"id": "FP-01",
"name": "تسجيل الدخول",
"description": "تغطي حالة الاستخدام \"تسجيل الدخول\" للفاعل المستخدم.",
"complexity": "معقدة",
"fpScore": 6
},
{
"id": "FP-02",
"name": "مراجعة الطلبات",
"description": "تغطي حالة الاستخدام \"مراجعة الطلبات\" للفاعل المدير.",
"complexity": "متوسطة",
"fpScore": 4
},
{
"id": "FP-03",
"name": "إصدار تقرير شهري",
"description": "تغطي حالة الاستخدام \"إصدار تقرير شهري\" للفاعل النظام.",
"complexity": "معقدة",
"fpScore": 6
},
{
"id": "FP-04",
"name": "إرسال طلب خدمة",
"description": "تغطي حالة الاستخدام \"إرسال طلب خدمة\" للفاعل العميل.",
"complexity": "متوسطة",
"fpScore": 4
}
],
"useCases": [
{
"id": "UC-01",
"title": "تسجيل الدخول",
"actor": "المستخدم",
"preconditions": "يجب أن يكون لدى المستخدم حساب صالح.",
"mainFlow": [
"يقوم المستخدم بإدخال اسم المستخدم وكلمة المرور.",
"يقوم النظام بالتحقق من صحة بيانات الاعتماد.",
"إذا كانت البيانات صحيحة، يتم تسجيل دخول المستخدم بنجاح.",
"إذا كانت البيانات غير صحيحة، يتم عرض رسالة خطأ."
],
"alternateFlow": [
"في حالة نسيان كلمة المرور، يمكن للمستخدم طلب إعادة تعيينها."
]
},
{
"id": "UC-02",
"title": "مراجعة الطلبات",
"actor": "المدير",
"preconditions": "يجب أن يكون لدى المدير صلاحيات الوصول إلى قسم مراجعة الطلبات.",
"mainFlow": [
"يقوم المدير بالوصول إلى قسم مراجعة الطلبات.",
"يعرض النظام قائمة بالطلبات المقدمة.",
"يمكن للمدير اختيار طلب معين لعرض تفاصيله.",
"يمكن للمدير تحديث حالة الطلب (مثل: قيد المعالجة، مكتمل، مرفوض)."
],
"alternateFlow": []
},
{
"id": "UC-03",
"title": "إصدار تقرير شهري",
"actor": "النظام",
"preconditions": "يجب أن يكون النظام قادراً على الوصول إلى بيانات الطلبات.",
"mainFlow": [
"في نهاية كل شهر، يقوم النظام بتشغيل عملية إنشاء التقرير.",
"يقوم النظام بتجميع بيانات الطلبات خلال الشهر.",
"يقوم النظام بإنشاء تقرير شهري يتضمن ملخصاً للطلبات وحالاتها."
],
"alternateFlow": [
"يمكن للمدير طلب إنشاء تقرير في أي وقت."
]
},
{
"id": "UC-04",
"title": "إرسال طلب خدمة",
"actor": "العميل",
"preconditions": "يجب أن يكون لدى العميل حساب أو القدرة على تقديم طلب كضيف.",
"mainFlow": [
"يقوم العميل بالوصول إلى واجهة تقديم طلب الخدمة.",
"يقوم العميل بإدخال تفاصيل الطلب (مثل: وصف الخدمة المطلوبة، معلومات الاتصال).",
"يقوم العميل بإرسال الطلب.",
"يقوم النظام بتسجيل الطلب وتعيين معرف فريد له.",
"يعرض النظام رسالة تأكيد للعميل."
],
"alternateFlow": []
}
],
"inputOutputs": [
{
"id": "IO-01",
"type": "input",
"name": "بيانات تسجيل الدخول (اسم المستخدم، كلمة المرور)",
"source": "المستخدم",
"destination": "النظام",
"description": "المعلومات التي يدخلها المستخدم لتسجيل الدخول."
},
{
"id": "IO-02",
"type": "output",
"name": "تأكيد تسجيل الدخول / رسالة خطأ",
"source": "النظام",
"destination": "المستخدم",
"description": "نتيجة عملية تسجيل الدخول."
},
{
"id": "IO-03",
"type": "input",
"name": "معرف الطلب / تحديث الحالة",
"source": "المدير",
"destination": "النظام",
"description": "المعلومات التي يدخلها المدير لتحديث حالة الطلب أو مراجعته."
},
{
"id": "IO-04",
"type": "output",
"name": "قائمة الطلبات / تفاصيل الطلب",
"source": "النظام",
"destination": "المدير",
"description": "البيانات التي يعرضها النظام للمدير لمراجعة الطلبات."
},
{
"id": "IO-05",
"type": "output",
"name": "التقرير الشهري",
"source": "النظام",
"destination": "المدير",
"description": "التقرير الذي يولده النظام شهرياً."
},
{
"id": "IO-06",
"type": "input",
"name": "تفاصيل طلب الخدمة (وصف، معلومات الاتصال)",
"source": "العميل",
"destination": "النظام",
"description": "المعلومات التي يقدمها العميل لإنشاء طلب خدمة."
},
{
"id": "IO-07",
"type": "output",
"name": "تأكيد تقديم الطلب",
"source": "النظام",
"destination": "العميل",
"description": "رسالة تأكيد للعميل بعد تقديم طلب الخدمة."
}
],
"isComplete": false
}
}
],
"srsDraft": {
"summary": "نظام لفحص واجهة الشات يسمح للمستخدمين بتسجيل الدخول، للمديرين بمراجعة الطلبات، وللعملاء بإرسال طلبات الخدمة، مع إصدار النظام لتقارير شهرية.",
"actors": [
"المستخدم",
"المدير",
"العميل",
"النظام"
],
"useCases": [
{
"id": "UC-01",
"title": "تسجيل الدخول",
"actor": "المستخدم",
"preconditions": "يجب أن يكون لدى المستخدم حساب صالح.",
"mainFlow": [
"يقوم المستخدم بإدخال اسم المستخدم وكلمة المرور.",
"يقوم النظام بالتحقق من صحة بيانات الاعتماد.",
"إذا كانت البيانات صحيحة، يتم تسجيل دخول المستخدم بنجاح.",
"إذا كانت البيانات غير صحيحة، يتم عرض رسالة خطأ."
],
"alternateFlow": [
"في حالة نسيان كلمة المرور، يمكن للمستخدم طلب إعادة تعيينها."
]
},
{
"id": "UC-02",
"title": "مراجعة الطلبات",
"actor": "المدير",
"preconditions": "يجب أن يكون لدى المدير صلاحيات الوصول إلى قسم مراجعة الطلبات.",
"mainFlow": [
"يقوم المدير بالوصول إلى قسم مراجعة الطلبات.",
"يعرض النظام قائمة بالطلبات المقدمة.",
"يمكن للمدير اختيار طلب معين لعرض تفاصيله.",
"يمكن للمدير تحديث حالة الطلب (مثل: قيد المعالجة، مكتمل، مرفوض)."
],
"alternateFlow": []
},
{
"id": "UC-03",
"title": "إصدار تقرير شهري",
"actor": "النظام",
"preconditions": "يجب أن يكون النظام قادراً على الوصول إلى بيانات الطلبات.",
"mainFlow": [
"في نهاية كل شهر، يقوم النظام بتشغيل عملية إنشاء التقرير.",
"يقوم النظام بتجميع بيانات الطلبات خلال الشهر.",
"يقوم النظام بإنشاء تقرير شهري يتضمن ملخصاً للطلبات وحالاتها."
],
"alternateFlow": [
"يمكن للمدير طلب إنشاء تقرير في أي وقت."
]
},
{
"id": "UC-04",
"title": "إرسال طلب خدمة",
"actor": "العميل",
"preconditions": "يجب أن يكون لدى العميل حساب أو القدرة على تقديم طلب كضيف.",
"mainFlow": [
"يقوم العميل بالوصول إلى واجهة تقديم طلب الخدمة.",
"يقوم العميل بإدخال تفاصيل الطلب (مثل: وصف الخدمة المطلوبة، معلومات الاتصال).",
"يقوم العميل بإرسال الطلب.",
"يقوم النظام بتسجيل الطلب وتعيين معرف فريد له.",
"يعرض النظام رسالة تأكيد للعميل."
],
"alternateFlow": []
}
],
"inputOutputs": [
{
"id": "IO-01",
"type": "input",
"name": "بيانات تسجيل الدخول (اسم المستخدم، كلمة المرور)",
"source": "المستخدم",
"destination": "النظام",
"description": "المعلومات التي يدخلها المستخدم لتسجيل الدخول."
},
{
"id": "IO-02",
"type": "output",
"name": "تأكيد تسجيل الدخول / رسالة خطأ",
"source": "النظام",
"destination": "المستخدم",
"description": "نتيجة عملية تسجيل الدخول."
},
{
"id": "IO-03",
"type": "input",
"name": "معرف الطلب / تحديث الحالة",
"source": "المدير",
"destination": "النظام",
"description": "المعلومات التي يدخلها المدير لتحديث حالة الطلب أو مراجعته."
},
{
"id": "IO-04",
"type": "output",
"name": "قائمة الطلبات / تفاصيل الطلب",
"source": "النظام",
"destination": "المدير",
"description": "البيانات التي يعرضها النظام للمدير لمراجعة الطلبات."
},
{
"id": "IO-05",
"type": "output",
"name": "التقرير الشهري",
"source": "النظام",
"destination": "المدير",
"description": "التقرير الذي يولده النظام شهرياً."
},
{
"id": "IO-06",
"type": "input",
"name": "تفاصيل طلب الخدمة (وصف، معلومات الاتصال)",
"source": "العميل",
"destination": "النظام",
"description": "المعلومات التي يقدمها العميل لإنشاء طلب خدمة."
},
{
"id": "IO-07",
"type": "output",
"name": "تأكيد تقديم الطلب",
"source": "النظام",
"destination": "العميل",
"description": "رسالة تأكيد للعميل بعد تقديم طلب الخدمة."
}
],
"functionPoints": [
{
"id": "FP-01",
"name": "تسجيل الدخول",
"description": "تغطي حالة الاستخدام \"تسجيل الدخول\" للفاعل المستخدم.",
"complexity": "معقدة",
"fpScore": 6
},
{
"id": "FP-02",
"name": "مراجعة الطلبات",
"description": "تغطي حالة الاستخدام \"مراجعة الطلبات\" للفاعل المدير.",
"complexity": "متوسطة",
"fpScore": 4
},
{
"id": "FP-03",
"name": "إصدار تقرير شهري",
"description": "تغطي حالة الاستخدام \"إصدار تقرير شهري\" للفاعل النظام.",
"complexity": "معقدة",
"fpScore": 6
},
{
"id": "FP-04",
"name": "إرسال طلب خدمة",
"description": "تغطي حالة الاستخدام \"إرسال طلب خدمة\" للفاعل العميل.",
"complexity": "متوسطة",
"fpScore": 4
}
],
"confidence": "high",
"question": "وجدت 4 Actors و4 Use Cases و3 Inputs و4 Outputs. هل تريد اعتمادها أم تعديلها؟",
"counts": {
"actors": 4,
"useCases": 4,
"inputs": 3,
"outputs": 4
}
},
"needsStructureConfirmation": false,
"isComplete": false,
"createdAt": "T08:50:59.345Z",
"updatedAt": "T08:51:08.433Z",
"stage": "evaluation"
}
]

View File

@ -4,6 +4,10 @@ const state = {
sessionId: null,
current: null,
drafts: [],
ui: {
workbenchOpen: false,
activeTab: 'srs',
},
};
const stageLabels = {
@ -14,6 +18,15 @@ const stageLabels = {
done: 'مكتمل',
};
const textareaLimits = {
input: { min: 46, max: 180 },
'srs-text': { min: 92, max: 240 },
'actors-editor': { min: 72, max: 180 },
'usecases-editor': { min: 72, max: 200 },
'io-editor': { min: 88, max: 220 },
'scope-editor': { min: 72, max: 180 },
};
document.addEventListener('DOMContentLoaded', init);
function init() {
@ -26,31 +39,268 @@ function init() {
sendMessage();
}
});
$('input').addEventListener('input', autoGrow);
$('srs-text').addEventListener('input', autoGrow);
$('actors-editor').addEventListener('input', autoGrow);
$('usecases-editor').addEventListener('input', autoGrow);
$('io-editor').addEventListener('input', autoGrow);
$('scope-editor').addEventListener('input', autoGrow);
['input', 'srs-text', 'actors-editor', 'usecases-editor', 'io-editor', 'scope-editor'].forEach((id) => {
$(id).addEventListener('input', autoGrow);
});
$('analyze-srs-btn').addEventListener('click', analyzeSrs);
$('confirm-srs-btn').addEventListener('click', confirmStructure);
$('save-planning-btn').addEventListener('click', savePlanning);
$('export-btn').addEventListener('click', () => {
if (state.sessionId) window.open(`/report/${state.sessionId}`, '_blank');
});
$('srs-file').addEventListener('change', () => {
const file = $('srs-file').files?.[0];
$('srs-file-name').textContent = file ? `الملف المختار: ${file.name}` : 'لم يتم اختيار ملف بعد.';
$('srs-file').addEventListener('change', updateSrsFileSelection);
configurePdfEngine();
updateSrsFileSelection();
$('workspace-collapse').addEventListener('click', () => setWorkbenchOpen(!state.ui.workbenchOpen));
document.querySelectorAll('[data-workspace-tab]').forEach((button) => {
button.addEventListener('click', () => setWorkbenchTab(button.dataset.workspaceTab, true));
});
setWorkbenchTab('srs');
setWorkbenchOpen(false);
resizeAllTextareas();
loadDrafts();
}
function configurePdfEngine() {
if (window.pdfjsLib?.GlobalWorkerOptions) {
window.pdfjsLib.GlobalWorkerOptions.workerSrc = '/vendor/pdfjs/pdf.worker.min.js';
}
}
function getSelectedSrsFile() {
return $('srs-file').files?.[0] || null;
}
function isPdfFile(file) {
return Boolean(file) && (/\.pdf$/i.test(file.name || '') || (file.type || '').toLowerCase() === 'application/pdf');
}
function updateSrsFileSelection(statusText = '') {
const file = getSelectedSrsFile();
const fileName = $('srs-file-name');
const note = $('srs-source-note');
if (statusText) {
fileName.textContent = statusText;
} else if (file) {
fileName.textContent = `الملف المختار: ${file.name}${isPdfFile(file) ? ' — PDF' : ''}`;
} else {
fileName.textContent = 'لم يتم اختيار ملف بعد.';
}
if (note) {
note.textContent = file && isPdfFile(file)
? 'سيتم استخراج النص من ملف PDF قبل التحليل. إذا كان الملف صورة ممسوحة ضوئياً فقد تحتاج إلى OCR أو لصق النص يدوياً.'
: 'يدعم TXT / MD / CSV / JSON / SRS / REQ إضافةً إلى PDF النصي. يمكنك أيضاً لصق المتطلبات مباشرة.';
}
}
function setSrsProcessingState(isBusy, statusText = '') {
const button = $('analyze-srs-btn');
button.disabled = Boolean(isBusy);
button.textContent = isBusy ? 'جارٍ تحليل المستند...' : 'حلّل الملف/النص';
updateSrsFileSelection(statusText);
}
async function readSrsSource({ file, manualText }) {
if (file) {
if (isPdfFile(file)) {
updateSrsFileSelection(`جارٍ استخراج النص من PDF: ${file.name}...`);
const content = await extractPdfText(file, ({ currentPage, totalPages }) => {
updateSrsFileSelection(`جارٍ استخراج النص من PDF: الصفحة ${currentPage}/${totalPages}${file.name}`);
});
if (!content.trim()) {
throw new Error('تعذر استخراج نص واضح من ملف PDF. إذا كان الملف صورة ممسوحة ضوئياً فاستخدم OCR أو الصق النص يدوياً.');
}
return content;
}
const content = await file.text();
if (looksBinary(content)) {
throw new Error('الملف المختار غير نصي مقروء. استخدم PDF نصي أو ملف TXT/MD/CSV/JSON، أو الصق النص يدوياً.');
}
return content;
}
return String(manualText || '').trim();
}
async function extractPdfText(file, onProgress) {
const pdfjs = window.pdfjsLib;
if (!pdfjs?.getDocument) {
throw new Error('محرّك قراءة PDF غير جاهز حالياً. حدّث الصفحة ثم جرّب مرة أخرى.');
}
pdfjs.GlobalWorkerOptions.workerSrc = '/vendor/pdfjs/pdf.worker.min.js';
const data = await file.arrayBuffer();
const pdf = await pdfjs.getDocument({ data }).promise;
const pages = [];
for (let pageNumber = 1; pageNumber <= pdf.numPages; pageNumber += 1) {
if (typeof onProgress === 'function') {
onProgress({ currentPage: pageNumber, totalPages: pdf.numPages });
}
const page = await pdf.getPage(pageNumber);
const textContent = await page.getTextContent();
const pageText = extractPdfPageText(textContent);
if (pageText) pages.push(pageText);
}
return pages.join('\n\n').replace(/\n{3,}/g, '\n\n').trim();
}
function extractPdfPageText(textContent) {
const lines = [];
let currentLine = [];
let lastY = null;
const flushLine = () => {
const line = currentLine.join(' ').replace(/\s+/g, ' ').trim();
if (line) lines.push(line);
currentLine = [];
};
(textContent?.items || []).forEach((item) => {
const raw = String(item?.str ?? '').replace(/\s+/g, ' ').trim();
const y = Number(item?.transform?.[5] ?? lastY ?? 0);
if (lastY !== null && Math.abs(y - lastY) > 4) {
flushLine();
}
if (raw) {
currentLine.push(raw);
lastY = y;
}
if (item?.hasEOL) {
flushLine();
lastY = null;
}
});
flushLine();
return lines.join('\n').trim();
}
function autoGrow(event) {
const target = event.target;
resizeTextarea(event.target);
}
function resizeTextarea(target) {
if (!target || target.tagName !== 'TEXTAREA') return;
target.style.height = 'auto';
target.style.height = Math.min(target.scrollHeight, target.classList.contains('editor-textarea') ? 220 : 160) + 'px';
const limits = textareaLimits[target.id] || { min: 72, max: 180 };
if (target.offsetParent === null) {
target.style.height = `${limits.min}px`;
target.style.overflowY = 'hidden';
return;
}
target.style.height = '0px';
const nextHeight = Math.max(limits.min, Math.min(target.scrollHeight, limits.max));
target.style.height = `${nextHeight}px`;
target.style.overflowY = target.scrollHeight > limits.max ? 'auto' : 'hidden';
}
function resizeAllTextareas() {
document.querySelectorAll('textarea').forEach((textarea) => resizeTextarea(textarea));
}
function setWorkbenchOpen(open, options = {}) {
const { activeTab, focusChat = false } = options;
if (activeTab) setWorkbenchTab(activeTab);
state.ui.workbenchOpen = Boolean(open);
const body = $('workspace-body');
const panel = $('workspace-panel');
const toggle = $('workspace-collapse');
const toggleText = $('workspace-collapse-text');
const stateBadge = $('workspace-state');
panel.classList.toggle('collapsed', !state.ui.workbenchOpen);
body.classList.toggle('hidden', !state.ui.workbenchOpen);
toggle.setAttribute('aria-expanded', state.ui.workbenchOpen ? 'true' : 'false');
toggleText.textContent = state.ui.workbenchOpen ? 'إخفاء اللوحة' : 'فتح اللوحة';
stateBadge.textContent = state.ui.workbenchOpen ? 'مفتوحة' : 'مطوية';
updateWorkbenchSummary(state.current);
if (state.ui.workbenchOpen) {
requestAnimationFrame(() => {
resizeAllTextareas();
focusWorkbenchField();
});
} else if (focusChat) {
requestAnimationFrame(() => $('input').focus());
}
}
function setWorkbenchTab(tab, openIfCollapsed = false) {
state.ui.activeTab = tab === 'proposal' ? 'proposal' : 'srs';
document.querySelectorAll('[data-workspace-tab]').forEach((button) => {
const active = button.dataset.workspaceTab === state.ui.activeTab;
button.classList.toggle('active', active);
button.setAttribute('aria-selected', active ? 'true' : 'false');
});
document.querySelectorAll('.workspace-card').forEach((panel) => {
const active = panel.dataset.panel === state.ui.activeTab;
panel.classList.toggle('active', active);
panel.hidden = !active;
});
if (openIfCollapsed && !state.ui.workbenchOpen) {
setWorkbenchOpen(true);
return;
}
if (state.ui.workbenchOpen) {
requestAnimationFrame(() => {
resizeAllTextareas();
focusWorkbenchField();
});
}
}
function focusWorkbenchField() {
const targetId = state.ui.activeTab === 'proposal' ? 'actors-editor' : 'srs-text';
$(targetId)?.focus();
}
function updateWorkbenchSummary(data = state.current) {
const summaryEl = $('workspace-summary');
if (!summaryEl) return;
if (!data) {
summaryEl.textContent = 'ارفع SRS أو راجع الاقتراح من هنا بدون أن تزاحم مساحة الشات.';
return;
}
const source = data.srsDraft || data;
const counts = source.counts || {
actors: (source.actors || []).length,
useCases: (source.useCases || []).length,
inputs: (source.inputOutputs || []).filter((item) => item.type === 'input').length,
outputs: (source.inputOutputs || []).filter((item) => item.type === 'output').length,
};
const parts = [];
parts.push(`المرحلة: ${stageLabels[data.stage] || data.stage || '—'}`);
if (counts.actors || counts.useCases || counts.inputs || counts.outputs) {
parts.push(`${counts.actors || 0} Actors · ${counts.useCases || 0} Use Cases · ${counts.inputs || 0} Inputs · ${counts.outputs || 0} Outputs`);
} else {
parts.push('ابدأ برفع SRS أو لصق المتطلبات لتوليد الاقتراح.');
}
parts.push(data.needsStructureConfirmation ? 'الاقتراح جاهز للمراجعة والاعتماد.' : 'يمكنك إبقاء اللوحة مطوية والتركيز على المحادثة.');
summaryEl.textContent = parts.join(' — ');
}
async function loadDrafts() {
@ -159,9 +409,12 @@ async function startSession() {
clearMessages();
hideResumeBanner();
showApp(title);
setWorkbenchTab('srs');
setWorkbenchOpen(false);
addAgentMessage(data.reply || 'تم بدء الجلسة.', true);
updateState(data);
await loadDrafts();
resizeAllTextareas();
$('input').focus();
} catch (error) {
alert(`خطأ: ${error.message}`);
@ -182,8 +435,14 @@ async function resumeSession(sessionId) {
showApp(data.title);
renderHistory(data.history || []);
updateState(data);
if (data.needsStructureConfirmation) {
setWorkbenchOpen(true, { activeTab: 'proposal' });
} else {
setWorkbenchTab('srs');
setWorkbenchOpen(false);
$('input').focus();
}
showResumeBanner(data.resumeMessage || 'أهلاً بك مجدداً، يمكننا متابعة العمل من آخر نقطة توقفنا عندها.');
$('input').focus();
await loadDrafts();
} catch (error) {
alert(`خطأ: ${error.message}`);
@ -201,7 +460,7 @@ async function sendMessage() {
hideResumeBanner();
addUserMessage(text);
input.value = '';
input.style.height = 'auto';
resizeTextarea(input);
$('send-btn').disabled = true;
const thinkingEl = addAgentMessage('يفكّر...', false, true);
@ -232,31 +491,25 @@ async function analyzeSrs() {
return;
}
const file = $('srs-file').files?.[0];
let content = $('srs-text').value.trim();
let filename = '';
if (!content && file) {
filename = file.name;
content = await file.text();
} else if (file) {
filename = file.name;
}
if (!content.trim()) {
alert('أرفق ملفاً نصياً بسيطاً أو الصق نص المتطلبات أولاً.');
return;
}
if (looksBinary(content)) {
alert('الملف يبدو غير نصي. في هذه النسخة يُفضَّل رفع ملف نصي بسيط أو لصق محتوى SRS يدوياً.');
return;
}
addUserMessage(filename ? `📄 تم إرسال ملف SRS للتحليل: ${filename}` : '📄 تم إرسال نص SRS للتحليل');
const thinkingEl = addAgentMessage('أحلّل المستند وأستخرج Actors وUse Cases وInputs/Outputs...', false, true);
const file = getSelectedSrsFile();
const filename = file?.name || '';
let thinkingEl = null;
try {
setSrsProcessingState(true);
const content = await readSrsSource({ file, manualText: $('srs-text').value.trim() });
if (!content.trim()) {
throw new Error('أرفق ملفاً أو الصق نص المتطلبات أولاً.');
}
if (looksBinary(content)) {
throw new Error('المحتوى المقروء غير صالح للتحليل كنص. استخدم PDF نصي أو ألصق النص مباشرة.');
}
addUserMessage(filename ? `📄 تم إرسال ملف SRS للتحليل: ${filename}` : '📄 تم إرسال نص SRS للتحليل');
thinkingEl = addAgentMessage('أحلّل المستند وأستخرج Actors وUse Cases وInputs/Outputs...', false, true);
const res = await fetch('/api/analyze-srs', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
@ -265,12 +518,21 @@ async function analyzeSrs() {
const data = await res.json();
if (!res.ok) throw new Error(data.error || 'فشل تحليل المستند');
thinkingEl.remove();
thinkingEl = null;
addAgentMessage(data.reply || 'تم تحليل المستند.', true);
updateState(data);
setWorkbenchTab('proposal');
setWorkbenchOpen(true);
await loadDrafts();
} catch (error) {
thinkingEl.querySelector('.bubble').textContent = `⚠️ ${error.message}`;
thinkingEl.classList.remove('thinking');
if (thinkingEl) {
thinkingEl.querySelector('.bubble').textContent = `⚠️ ${error.message}`;
thinkingEl.classList.remove('thinking');
} else {
alert(`خطأ: ${error.message}`);
}
} finally {
setSrsProcessingState(false);
}
}
@ -302,6 +564,7 @@ async function confirmStructure() {
addAgentMessage(data.reply || 'تم اعتماد الهيكل الأولي.', true);
updateState(data);
await loadDrafts();
setWorkbenchOpen(false, { focusChat: true });
} catch (error) {
thinkingEl.querySelector('.bubble').textContent = `⚠️ ${error.message}`;
thinkingEl.classList.remove('thinking');
@ -418,6 +681,8 @@ function updateState(data) {
fillProposalEditors(data);
renderTree(actors, fps, ios);
renderPreview(data);
updateWorkbenchSummary(data);
resizeAllTextareas();
if (data.isComplete || ucs.length >= 1 || fps.length >= 2) $('export-btn').classList.remove('hidden');
}

View File

@ -111,76 +111,106 @@
<main class="stage">
<div class="stage-header">
<div class="stage-title">جلسة تحليل المتطلبات والتقدير</div>
<div class="stage-heading">
<div class="stage-title">جلسة تحليل المتطلبات والتقدير</div>
<div class="stage-subtitle">الشات هو المساحة الأساسية، ولوحة العمل تظهر فقط عند الحاجة.</div>
</div>
<div class="stage-pill" id="stage-pill">تمهيد</div>
</div>
<div id="resume-banner" class="resume-banner glass hidden"></div>
<section class="tool-grid">
<div class="tool-card glass">
<div class="tool-header">
<div>
<div class="tool-title">رفع ملف SRS أو نص متطلبات</div>
<div class="tool-subtitle">يدعم الملفات النصية البسيطة، مع إمكانية اللصق اليدوي.</div>
<section id="workspace-panel" class="workspace-panel glass collapsed">
<div class="workspace-bar">
<div class="workspace-meta">
<div class="workspace-title-row">
<div class="workspace-title">لوحة العمل المدمجة</div>
<span id="workspace-state" class="workspace-state-badge">مطوية</span>
</div>
<span class="tool-badge">LLM + Heuristic</span>
<div id="workspace-summary" class="workspace-summary">ارفع SRS أو راجع الاقتراح من هنا بدون أن تزاحم مساحة الشات.</div>
</div>
<div class="tool-actions">
<label class="file-label" for="srs-file">اختر ملفاً</label>
<input id="srs-file" type="file" accept=".txt,.md,.csv,.json,.srs,.req,.text">
<button id="analyze-srs-btn" class="btn-secondary">حلّل الملف/النص</button>
<div class="workspace-actions">
<div class="workspace-tabs" role="tablist" aria-label="لوحة العمل">
<button id="workspace-tab-srs" class="workspace-tab active" type="button" data-workspace-tab="srs" aria-selected="true">SRS</button>
<button id="workspace-tab-proposal" class="workspace-tab" type="button" data-workspace-tab="proposal" aria-selected="false">الاقتراح</button>
</div>
<button id="workspace-collapse" class="workspace-collapse-btn" type="button" aria-expanded="false" aria-controls="workspace-body">
<span id="workspace-collapse-text">فتح اللوحة</span>
</button>
</div>
<div id="srs-file-name" class="hint-line">لم يتم اختيار ملف بعد.</div>
<textarea id="srs-text" class="tool-textarea" placeholder="أو الصق هنا مقتطفات SRS / المتطلبات الوظيفية والنطاق العام..." rows="6"></textarea>
</div>
<div class="tool-card glass">
<div class="tool-header">
<div>
<div class="tool-title">الاقتراح الأولي القابل للتعديل</div>
<div class="tool-subtitle">عدّل القوائم ثم اضغط تأكيد لاعتماد الهيكل الأولي.</div>
<div id="workspace-body" class="workspace-body hidden">
<div class="tool-panels">
<div class="tool-card workspace-card active" data-panel="srs" role="tabpanel" aria-labelledby="workspace-tab-srs">
<div class="tool-header">
<div>
<div class="tool-title">رفع ملف SRS أو نص متطلبات</div>
<div class="tool-subtitle">يدعم TXT / MD / CSV / JSON / SRS / REQ إضافةً إلى PDF النصي، مع إمكانية اللصق اليدوي.</div>
</div>
<span class="tool-badge">LLM + Heuristic</span>
</div>
<div class="tool-actions">
<label class="file-label" for="srs-file">اختر ملفاً</label>
<input id="srs-file" type="file" accept=".pdf,application/pdf,.txt,.md,.csv,.json,.srs,.req,.text">
<button id="analyze-srs-btn" class="btn-secondary">حلّل الملف/النص</button>
</div>
<div id="srs-file-name" class="hint-line">لم يتم اختيار ملف بعد.</div>
<div id="srs-source-note" class="hint-line">يدعم PDF النصي أيضاً. إذا كان الملف عبارة عن صور ممسوحة ضوئياً فستحتاج OCR أو لصق النص يدوياً.</div>
<textarea id="srs-text" class="tool-textarea" placeholder="أو الصق هنا مقتطفات SRS / المتطلبات الوظيفية والنطاق العام..." rows="4"></textarea>
</div>
<span id="proposal-status" class="tool-badge dim">بانتظار تحليل</span>
</div>
<div id="proposal-summary" class="hint-line">ستظهر هنا أعداد Actors وUse Cases وInputs/Outputs بعد تحليل المستند.</div>
<div class="tool-card workspace-card" data-panel="proposal" role="tabpanel" aria-labelledby="workspace-tab-proposal" hidden>
<div class="tool-header">
<div>
<div class="tool-title">الاقتراح الأولي القابل للتعديل</div>
<div class="tool-subtitle">عدّل القوائم ثم اضغط تأكيد لاعتماد الهيكل الأولي.</div>
</div>
<span id="proposal-status" class="tool-badge dim">بانتظار تحليل</span>
</div>
<div class="proposal-grid">
<label class="editor-block">
<span>Actors — عنصر واحد في كل سطر</span>
<textarea id="actors-editor" class="editor-textarea" placeholder="المستخدم&#10;المدير&#10;نظام خارجي"></textarea>
</label>
<label class="editor-block">
<span>Use Cases — الصيغة: العنوان | الفاعل</span>
<textarea id="usecases-editor" class="editor-textarea" placeholder="تسجيل طلب جديد | العميل&#10;اعتماد الطلب | المدير"></textarea>
</label>
<label class="editor-block wide">
<span>Inputs / Outputs — الصيغة: Input أو Output | الاسم | المصدر | الوجهة | الوصف</span>
<textarea id="io-editor" class="editor-textarea io" placeholder="Input | بيانات الطلب | العميل | النظام | بيانات الطلب الأساسية&#10;Output | تقرير شهري | النظام | المدير | تقرير أداء المبيعات"></textarea>
</label>
<label class="editor-block wide">
<span>ملخص النطاق</span>
<textarea id="scope-editor" class="editor-textarea" placeholder="ملخص نطاق النظام المتوقع..."></textarea>
</label>
</div>
<div id="proposal-summary" class="hint-line">ستظهر هنا أعداد Actors وUse Cases وInputs/Outputs بعد تحليل المستند.</div>
<div class="tool-actions compact-actions">
<button id="confirm-srs-btn" class="btn-secondary gold">تأكيد واعتماد الاقتراح</button>
<div class="proposal-grid">
<label class="editor-block">
<span>Actors — عنصر واحد في كل سطر</span>
<textarea id="actors-editor" class="editor-textarea" placeholder="المستخدم&#10;المدير&#10;نظام خارجي"></textarea>
</label>
<label class="editor-block">
<span>Use Cases — الصيغة: العنوان | الفاعل</span>
<textarea id="usecases-editor" class="editor-textarea" placeholder="تسجيل طلب جديد | العميل&#10;اعتماد الطلب | المدير"></textarea>
</label>
<label class="editor-block wide">
<span>Inputs / Outputs — الصيغة: Input أو Output | الاسم | المصدر | الوجهة | الوصف</span>
<textarea id="io-editor" class="editor-textarea io" placeholder="Input | بيانات الطلب | العميل | النظام | بيانات الطلب الأساسية&#10;Output | تقرير شهري | النظام | المدير | تقرير أداء المبيعات"></textarea>
</label>
<label class="editor-block wide">
<span>ملخص النطاق</span>
<textarea id="scope-editor" class="editor-textarea" placeholder="ملخص نطاق النظام المتوقع..."></textarea>
</label>
</div>
<div class="tool-actions compact-actions">
<button id="confirm-srs-btn" class="btn-secondary gold">تأكيد واعتماد الاقتراح</button>
</div>
</div>
</div>
</div>
</section>
<div id="messages" class="messages"></div>
<section class="chat-shell glass">
<div id="messages" class="messages"></div>
<div class="composer glass">
<textarea id="input" placeholder="اكتب ردك هنا... أو اطلب تعديل المقترحات أو سؤالاً جديداً" rows="1"></textarea>
<button id="send-btn" class="send-btn" title="إرسال">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 12l14-7-7 14-2-5-5-2z"/></svg>
</button>
</div>
<div class="composer">
<textarea id="input" placeholder="اكتب ردك هنا... أو اطلب تعديل المقترحات أو سؤالاً جديداً" rows="1"></textarea>
<button id="send-btn" class="send-btn" title="إرسال">
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M5 12l14-7-7 14-2-5-5-2z"/></svg>
</button>
</div>
</section>
</main>
<aside class="preview glass">
@ -194,6 +224,7 @@
</aside>
</div>
<script src="/vendor/pdfjs/pdf.min.js"></script>
<script src="/app.js"></script>
</body>
</html>

View File

@ -371,3 +371,375 @@ body{
.proposal-grid,.inline-fields,.stat-grid-2,.summary-strip{grid-template-columns:1fr}
.welcome-card{max-width:100%}
}
/* === Focus-mode redesign: larger centered chat + collapsible workbench === */
:root{
--shell-gap:14px;
--sidebar-width:236px;
--preview-width:280px;
--stage-max:1120px;
}
.app-shell{
grid-template-columns:var(--sidebar-width) minmax(0,1.65fr) var(--preview-width);
gap:var(--shell-gap);
padding:14px;
}
.sidebar,
.preview{
padding:18px;
border-radius:24px;
}
.sidebar{gap:2px}
.preview{min-width:0}
.stage{
min-width:0;
gap:12px;
max-width:var(--stage-max);
}
.sidebar-header{margin-bottom:16px}
.brand-sub{max-width:160px}
.stat-card{padding:14px;margin-bottom:10px;border-radius:16px}
.stat-value{font-size:28px}
.stat-value.mini{font-size:22px}
.tree-section{margin-top:4px}
.drafts-sidebar{margin-top:12px}
.draft-list{gap:8px}
.draft-card{padding:11px 12px;border-radius:12px}
.draft-title{font-size:12.5px}
.draft-meta,
.draft-date,
.draft-stats{font-size:10.8px}
.btn-export{margin-top:14px;padding:12px;font-size:13px}
.stage-header{
display:flex;
align-items:flex-start;
justify-content:space-between;
gap:12px;
padding:0 2px;
}
.stage-heading{
display:flex;
flex-direction:column;
gap:4px;
min-width:0;
}
.stage-title{font-size:13px;color:var(--pearl-dim);font-weight:600}
.stage-subtitle{font-size:12px;line-height:1.7;color:rgba(244,241,234,.7)}
.stage-pill{padding:6px 12px;white-space:nowrap}
.resume-banner{
padding:11px 14px;
border-radius:18px;
}
.workspace-panel{
padding:14px 16px;
display:flex;
flex-direction:column;
gap:12px;
overflow:hidden;
}
.workspace-panel.collapsed{
padding-bottom:12px;
}
.workspace-bar{
display:flex;
align-items:flex-start;
justify-content:space-between;
gap:14px;
}
.workspace-meta{
min-width:0;
display:flex;
flex-direction:column;
gap:6px;
}
.workspace-title-row{
display:flex;
align-items:center;
gap:10px;
flex-wrap:wrap;
}
.workspace-title{
font-size:13px;
font-weight:700;
color:var(--pearl);
}
.workspace-state-badge{
display:inline-flex;
align-items:center;
gap:6px;
padding:4px 10px;
border-radius:999px;
background:rgba(255,255,255,.05);
border:1px solid rgba(255,255,255,.08);
color:var(--pearl-dim);
font-size:10.5px;
font-weight:700;
}
.workspace-summary{
font-size:11.5px;
line-height:1.8;
color:var(--pearl-dim);
max-width:720px;
}
.workspace-actions{
display:flex;
align-items:center;
justify-content:flex-end;
gap:8px;
flex-wrap:wrap;
}
.workspace-tabs{
display:inline-flex;
align-items:center;
gap:6px;
padding:4px;
background:rgba(0,0,0,.22);
border:1px solid rgba(255,255,255,.05);
border-radius:999px;
}
.workspace-tab,
.workspace-collapse-btn{
border:none;
cursor:pointer;
font-family:inherit;
transition:.2s ease;
}
.workspace-tab{
min-width:88px;
padding:9px 14px;
border-radius:999px;
background:transparent;
color:var(--pearl-dim);
font-size:12px;
font-weight:700;
}
.workspace-tab:hover{
color:var(--pearl);
}
.workspace-tab.active{
background:linear-gradient(135deg,rgba(201,168,76,.24),rgba(201,168,76,.14));
color:var(--gold-soft);
box-shadow:0 8px 22px rgba(0,0,0,.18);
}
.workspace-collapse-btn{
padding:10px 14px;
border-radius:14px;
background:rgba(255,255,255,.06);
color:var(--pearl);
border:1px solid rgba(255,255,255,.08);
font-size:12px;
font-weight:700;
}
.workspace-collapse-btn:hover{
border-color:rgba(201,168,76,.35);
transform:translateY(-1px);
}
.workspace-body{
max-height:min(40vh, 430px);
overflow:auto;
padding-top:4px;
}
.workspace-body.hidden{
display:none !important;
}
.tool-panels{
display:flex;
flex-direction:column;
gap:12px;
}
.workspace-card{
display:none;
padding:14px;
min-height:0;
overflow:auto;
border-radius:18px;
background:rgba(255,255,255,.025);
border:1px solid rgba(255,255,255,.06);
box-shadow:inset 0 1px 0 rgba(255,255,255,.03);
}
.workspace-card.active{
display:flex;
}
.tool-header{gap:10px}
.tool-title{font-size:13.5px}
.tool-subtitle{font-size:11.5px}
.tool-actions{gap:8px}
.tool-actions.compact-actions{justify-content:flex-start}
.tool-badge{font-size:10px;padding:6px 9px}
.hint-line{margin-top:6px}
.tool-textarea,
.editor-textarea{
padding:10px 12px;
min-height:72px;
resize:none;
line-height:1.75;
}
.tool-textarea{
min-height:92px;
}
.editor-textarea.io{
min-height:88px;
}
.proposal-grid{gap:10px}
.editor-block{gap:5px;font-size:11.5px}
.file-label{padding:9px 13px}
.btn-secondary{padding:10px 13px}
.chat-shell{
flex:1;
min-height:0;
display:flex;
flex-direction:column;
overflow:hidden;
padding:0;
border-radius:26px;
}
.messages{
flex:1;
min-height:0;
overflow-y:auto;
padding:24px 18px 16px;
display:flex;
flex-direction:column;
gap:18px;
background:transparent;
border:none;
border-radius:0;
scroll-behavior:smooth;
}
.msg{gap:12px}
.avatar{width:32px;height:32px;font-size:12px}
.bubble{
max-width:min(86%, 900px);
padding:15px 18px;
line-height:1.95;
font-size:15px;
border-radius:18px;
}
.msg.agent .bubble{
background:rgba(255,255,255,.02);
border:1px solid rgba(255,255,255,.05);
}
.msg.user .bubble{
background:rgba(124,92,255,.1);
border:1px solid rgba(124,92,255,.18);
}
.composer{
display:flex;
align-items:flex-end;
gap:12px;
padding:12px 16px 14px;
border-top:1px solid rgba(255,255,255,.06);
background:rgba(255,255,255,.025);
}
#input{
padding:8px 0;
font-size:15px;
line-height:1.75;
max-height:180px;
}
.send-btn{width:44px;height:44px}
.preview-header{margin-bottom:12px}
.preview-content{font-size:12px;line-height:1.8}
.summary-strip{gap:8px}
.preview-metric{padding:11px}
.metric-value{font-size:20px}
.io-prev,
.preview-content .uc-prev,
.analysis-card,
.sprint-card,
.milestone-item{border-radius:10px}
@media (max-width:1380px){
.app-shell{
grid-template-columns:220px minmax(0,1fr) 250px;
}
.preview,
.sidebar{padding:16px}
}
@media (max-width:1120px){
.app-shell{
grid-template-columns:220px minmax(0,1fr);
}
.preview{display:none}
.stage{max-width:none}
}
@media (max-width:860px){
.app-shell{
grid-template-columns:1fr;
padding:10px;
gap:10px;
}
.sidebar{display:none}
.stage-header{
flex-direction:column;
align-items:stretch;
}
.workspace-bar,
.workspace-actions{
flex-direction:column;
align-items:stretch;
}
.workspace-tabs{
width:100%;
justify-content:space-between;
}
.workspace-tab{
flex:1;
min-width:0;
}
.workspace-body{
max-height:min(44vh, 460px);
}
.messages{
padding:18px 12px 14px;
}
.bubble{
max-width:92%;
font-size:14.5px;
}
}

22
public/vendor/pdfjs/pdf.min.js vendored Normal file

File diff suppressed because one or more lines are too long

22
public/vendor/pdfjs/pdf.worker.min.js vendored Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

Binary file not shown.