mmotahari commited on
Commit
6c0f4ec
·
verified ·
1 Parent(s): d1255fa

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +46 -45
app.py CHANGED
@@ -7,20 +7,20 @@ HTML_APP = """
7
  #planner-root {
8
  font-family: 'Vazirmatn', sans-serif;
9
  direction: rtl;
10
- background: #0d1117;
11
- color: #e6edf3;
12
  padding: 32px 20px 60px;
13
  min-height: 100vh;
14
  }
15
  #planner-root h1 {
16
  text-align: center;
17
  font-size: 2rem; font-weight: 800;
18
- background: linear-gradient(135deg, #58a6ff, #bc8cff);
19
  -webkit-background-clip: text; -webkit-text-fill-color: transparent;
20
  margin: 0 0 6px;
21
  }
22
  #planner-root .sub {
23
- text-align: center; color: #8b949e;
24
  font-size: 0.88rem; margin-bottom: 28px;
25
  }
26
  .row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-bottom: 14px; }
@@ -28,12 +28,12 @@ HTML_APP = """
28
 
29
  .field label {
30
  display: block; font-size: 0.85rem; font-weight: 600;
31
- color: #8b949e; margin-bottom: 6px;
32
  }
33
  .field textarea, .field input {
34
- width: 100%; background: #161b22;
35
- border: 1px solid #30363d; border-radius: 8px;
36
- color: #e6edf3; font-family: 'Vazirmatn', sans-serif;
37
  font-size: 0.95rem; padding: 10px 12px;
38
  transition: border-color .2s, box-shadow .2s;
39
  resize: vertical;
@@ -41,65 +41,67 @@ HTML_APP = """
41
  .field textarea { min-height: 80px; }
42
  .field input[type=password] { height: 44px; }
43
  .field textarea:focus, .field input:focus {
44
- outline: none; border-color: #58a6ff;
45
- box-shadow: 0 0 0 3px rgba(88,166,255,.12);
46
  }
47
 
48
  #startBtn {
49
  width: 100%; padding: 13px;
50
- background: linear-gradient(135deg, #7c3aed, #4f46e5);
51
  border: none; border-radius: 8px; color: #fff;
52
  font-family: 'Vazirmatn', sans-serif;
53
  font-weight: 700; font-size: 1.05rem;
54
  cursor: pointer; margin-bottom: 24px;
55
- box-shadow: 0 4px 18px rgba(124,58,237,.35);
56
  transition: all .2s;
57
  }
58
- #startBtn:hover:not(:disabled) { filter: brightness(1.12); transform: translateY(-1px); }
59
  #startBtn:disabled { opacity: .55; cursor: not-allowed; transform: none; }
60
 
61
  .step {
62
  border-radius: 10px; margin: 10px 0;
63
- border: 1px solid #21262d; overflow: hidden;
64
  transition: border-color .3s, box-shadow .3s;
 
65
  }
66
- .step.s-pending { opacity: .4; }
67
- .step.s-active { border-color: #58a6ff; box-shadow: 0 0 0 3px rgba(88,166,255,.1); }
68
- .step.s-done { border-color: #238636; }
69
- .step.s-error { border-color: #f85149; }
70
 
71
  .step-hd {
72
  display: flex; align-items: center; gap: 10px;
73
  padding: 12px 16px; font-weight: 600; font-size: .93rem;
74
- background: #161b22;
 
75
  }
76
- .step.s-active .step-hd { background: #1c2a3a; color: #58a6ff; }
77
- .step.s-done .step-hd { background: #0f2d16; color: #3fb950; }
78
- .step.s-error .step-hd { background: #2d1515; color: #f85149; }
79
 
80
  .step-body {
81
- padding: 14px 16px; font-size: .87rem; line-height: 1.85;
82
- background: #0d1117; color: #c9d1d9;
83
- border-top: 1px solid #21262d;
84
  white-space: pre-wrap; display: none;
85
  }
86
  .step.s-active .step-body,
87
  .step.s-done .step-body,
88
  .step.s-error .step-body { display: block; }
89
- .step.s-active .step-body { color: #8b949e; font-style: italic; }
 
90
 
91
  @keyframes spin { to { transform: rotate(360deg); } }
92
  .spin-icon { display: inline-block; animation: spin 1s linear infinite; }
93
 
94
  #doneBanner {
95
  text-align: center; margin-top: 18px; padding: 15px;
96
- background: #0f2d16; border: 1px solid #238636;
97
- border-radius: 10px; color: #3fb950;
98
  font-weight: 700; font-size: 1rem; display: none;
99
  }
100
  #errBox {
101
- background: #2d1515; border: 1px solid #f85149;
102
- color: #f85149; padding: 12px 16px; border-radius: 8px;
103
  font-weight: 600; margin-bottom: 14px; display: none;
104
  font-size: .9rem;
105
  }
@@ -156,23 +158,24 @@ const STEPS = [
156
  }
157
  ];
158
 
159
- function setStep(idx, state, bodyText) {
160
  const area = document.getElementById("stepsArea");
161
- // build if needed
162
- if (area.children.length === 0) {
163
- STEPS.forEach((s, i) => {
164
- const d = document.createElement("div");
165
- d.className = "step s-pending";
166
- d.id = "step-" + i;
167
- d.innerHTML = `<div class="step-hd"><span class="icon">⬜</span>&nbsp;${s.title}</div><div class="step-body"></div>`;
168
- area.appendChild(d);
169
- });
170
- }
 
171
  const el = document.getElementById("step-" + idx);
172
  if (!el) return;
173
  el.className = "step s-" + state;
174
  const icons = { pending:"⬜", active:'<span class="spin-icon">⏳</span>', done:"✅", error:"❌" };
175
- el.querySelector(".step-hd").innerHTML = `<span class="icon">${icons[state]}</span>&nbsp;${STEPS[idx].title}`;
176
  if (bodyText !== undefined) el.querySelector(".step-body").textContent = bodyText;
177
  }
178
 
@@ -209,7 +212,6 @@ async function startPlanning() {
209
  const btn = document.getElementById("startBtn");
210
  document.getElementById("errBox").style.display = "none";
211
  document.getElementById("doneBanner").style.display = "none";
212
- document.getElementById("stepsArea").innerHTML = "";
213
 
214
  if (!goal) { showErr("هدف را وارد کنید"); return; }
215
  if (!apiKey) { showErr("API Key را وارد کنید"); return; }
@@ -217,8 +219,7 @@ async function startPlanning() {
217
  btn.disabled = true;
218
  btn.textContent = "⏳ در حال برنامه‌ریزی...";
219
 
220
- // init all as pending
221
- STEPS.forEach((_,i) => setStep(i, "pending", ""));
222
 
223
  const ctx = { goal };
224
  for (let i = 0; i < STEPS.length; i++) {
 
7
  #planner-root {
8
  font-family: 'Vazirmatn', sans-serif;
9
  direction: rtl;
10
+ background: #f5f7fa;
11
+ color: #1a202c;
12
  padding: 32px 20px 60px;
13
  min-height: 100vh;
14
  }
15
  #planner-root h1 {
16
  text-align: center;
17
  font-size: 2rem; font-weight: 800;
18
+ background: linear-gradient(135deg, #4f46e5, #7c3aed);
19
  -webkit-background-clip: text; -webkit-text-fill-color: transparent;
20
  margin: 0 0 6px;
21
  }
22
  #planner-root .sub {
23
+ text-align: center; color: #718096;
24
  font-size: 0.88rem; margin-bottom: 28px;
25
  }
26
  .row { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-bottom: 14px; }
 
28
 
29
  .field label {
30
  display: block; font-size: 0.85rem; font-weight: 600;
31
+ color: #4a5568; margin-bottom: 6px;
32
  }
33
  .field textarea, .field input {
34
+ width: 100%; background: #ffffff;
35
+ border: 1px solid #cbd5e0; border-radius: 8px;
36
+ color: #1a202c; font-family: 'Vazirmatn', sans-serif;
37
  font-size: 0.95rem; padding: 10px 12px;
38
  transition: border-color .2s, box-shadow .2s;
39
  resize: vertical;
 
41
  .field textarea { min-height: 80px; }
42
  .field input[type=password] { height: 44px; }
43
  .field textarea:focus, .field input:focus {
44
+ outline: none; border-color: #667eea;
45
+ box-shadow: 0 0 0 3px rgba(102,126,234,.15);
46
  }
47
 
48
  #startBtn {
49
  width: 100%; padding: 13px;
50
+ background: linear-gradient(135deg, #667eea, #764ba2);
51
  border: none; border-radius: 8px; color: #fff;
52
  font-family: 'Vazirmatn', sans-serif;
53
  font-weight: 700; font-size: 1.05rem;
54
  cursor: pointer; margin-bottom: 24px;
55
+ box-shadow: 0 4px 18px rgba(102,126,234,.35);
56
  transition: all .2s;
57
  }
58
+ #startBtn:hover:not(:disabled) { filter: brightness(1.08); transform: translateY(-1px); }
59
  #startBtn:disabled { opacity: .55; cursor: not-allowed; transform: none; }
60
 
61
  .step {
62
  border-radius: 10px; margin: 10px 0;
63
+ border: 1px solid #e2e8f0; overflow: hidden;
64
  transition: border-color .3s, box-shadow .3s;
65
+ background: #ffffff;
66
  }
67
+ .step.s-pending { opacity: .45; }
68
+ .step.s-active { border-color: #667eea; box-shadow: 0 0 0 3px rgba(102,126,234,.12); }
69
+ .step.s-done { border-color: #48bb78; }
70
+ .step.s-error { border-color: #fc8181; }
71
 
72
  .step-hd {
73
  display: flex; align-items: center; gap: 10px;
74
  padding: 12px 16px; font-weight: 600; font-size: .93rem;
75
+ background: #f7fafc; color: #2d3748;
76
+ border-bottom: 1px solid #e2e8f0;
77
  }
78
+ .step.s-active .step-hd { background: #ebf4ff; color: #3c5fb8; border-color: #bee3f8; }
79
+ .step.s-done .step-hd { background: #f0fff4; color: #276749; border-color: #c6f6d5; }
80
+ .step.s-error .step-hd { background: #fff5f5; color: #c53030; border-color: #fed7d7; }
81
 
82
  .step-body {
83
+ padding: 14px 16px; font-size: .88rem; line-height: 1.85;
84
+ background: #ffffff; color: #2d3748;
 
85
  white-space: pre-wrap; display: none;
86
  }
87
  .step.s-active .step-body,
88
  .step.s-done .step-body,
89
  .step.s-error .step-body { display: block; }
90
+ .step.s-active .step-body { color: #a0aec0; font-style: italic; }
91
+ .step.s-error .step-body { color: #c53030; }
92
 
93
  @keyframes spin { to { transform: rotate(360deg); } }
94
  .spin-icon { display: inline-block; animation: spin 1s linear infinite; }
95
 
96
  #doneBanner {
97
  text-align: center; margin-top: 18px; padding: 15px;
98
+ background: #f0fff4; border: 1px solid #68d391;
99
+ border-radius: 10px; color: #276749;
100
  font-weight: 700; font-size: 1rem; display: none;
101
  }
102
  #errBox {
103
+ background: #fff5f5; border: 1px solid #fc8181;
104
+ color: #c53030; padding: 12px 16px; border-radius: 8px;
105
  font-weight: 600; margin-bottom: 14px; display: none;
106
  font-size: .9rem;
107
  }
 
158
  }
159
  ];
160
 
161
+ function buildSteps() {
162
  const area = document.getElementById("stepsArea");
163
+ area.innerHTML = "";
164
+ STEPS.forEach((s, i) => {
165
+ const d = document.createElement("div");
166
+ d.className = "step s-pending";
167
+ d.id = "step-" + i;
168
+ d.innerHTML = `<div class="step-hd"><span>⬜</span>&nbsp;${s.title}</div><div class="step-body"></div>`;
169
+ area.appendChild(d);
170
+ });
171
+ }
172
+
173
+ function setStep(idx, state, bodyText) {
174
  const el = document.getElementById("step-" + idx);
175
  if (!el) return;
176
  el.className = "step s-" + state;
177
  const icons = { pending:"⬜", active:'<span class="spin-icon">⏳</span>', done:"✅", error:"❌" };
178
+ el.querySelector(".step-hd").innerHTML = `<span>${icons[state]}</span>&nbsp;${STEPS[idx].title}`;
179
  if (bodyText !== undefined) el.querySelector(".step-body").textContent = bodyText;
180
  }
181
 
 
212
  const btn = document.getElementById("startBtn");
213
  document.getElementById("errBox").style.display = "none";
214
  document.getElementById("doneBanner").style.display = "none";
 
215
 
216
  if (!goal) { showErr("هدف را وارد کنید"); return; }
217
  if (!apiKey) { showErr("API Key را وارد کنید"); return; }
 
219
  btn.disabled = true;
220
  btn.textContent = "⏳ در حال برنامه‌ریزی...";
221
 
222
+ buildSteps();
 
223
 
224
  const ctx = { goal };
225
  for (let i = 0; i < STEPS.length; i++) {