Almirah 0.4.1 → 0.4.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/bin/almirah +2 -2
  3. data/lib/almirah/doc_fabric.rb +8 -0
  4. data/lib/almirah/doc_items/blockquote.rb +15 -16
  5. data/lib/almirah/doc_items/code_block.rb +19 -21
  6. data/lib/almirah/doc_items/controlled_paragraph.rb +2 -2
  7. data/lib/almirah/doc_items/controlled_table.rb +11 -11
  8. data/lib/almirah/doc_items/controlled_table_row.rb +15 -18
  9. data/lib/almirah/doc_items/doc_footer.rb +10 -13
  10. data/lib/almirah/doc_items/doc_item.rb +2 -2
  11. data/lib/almirah/doc_items/heading.rb +21 -3
  12. data/lib/almirah/doc_items/image.rb +25 -20
  13. data/lib/almirah/doc_items/markdown_list.rb +2 -2
  14. data/lib/almirah/doc_items/markdown_table.rb +10 -3
  15. data/lib/almirah/doc_items/scope_table.rb +188 -0
  16. data/lib/almirah/doc_items/text_line.rb +52 -27
  17. data/lib/almirah/doc_items/todo_block.rb +15 -16
  18. data/lib/almirah/doc_items/work_item.rb +129 -0
  19. data/lib/almirah/doc_parser.rb +14 -8
  20. data/lib/almirah/doc_types/base_document.rb +21 -5
  21. data/lib/almirah/doc_types/coverage.rb +3 -3
  22. data/lib/almirah/doc_types/critical_chain_page.rb +218 -0
  23. data/lib/almirah/doc_types/decision.rb +158 -7
  24. data/lib/almirah/doc_types/decision_grouping.rb +17 -0
  25. data/lib/almirah/doc_types/decisions_overview.rb +593 -32
  26. data/lib/almirah/doc_types/implementation.rb +98 -98
  27. data/lib/almirah/doc_types/index.rb +11 -12
  28. data/lib/almirah/doc_types/persistent_document.rb +1 -1
  29. data/lib/almirah/doc_types/planning_dates.rb +17 -0
  30. data/lib/almirah/doc_types/protocol.rb +16 -20
  31. data/lib/almirah/doc_types/source_file.rb +1 -1
  32. data/lib/almirah/doc_types/traceability.rb +124 -133
  33. data/lib/almirah/dom/doc_section.rb +2 -2
  34. data/lib/almirah/dom/document.rb +1 -1
  35. data/lib/almirah/html_safe.rb +39 -0
  36. data/lib/almirah/link_registry.rb +21 -6
  37. data/lib/almirah/navigation_pane.rb +9 -13
  38. data/lib/almirah/project/critical_chain.rb +117 -0
  39. data/lib/almirah/project/doc_linker.rb +4 -4
  40. data/lib/almirah/project/fever_chart.rb +94 -0
  41. data/lib/almirah/project/project_data.rb +8 -2
  42. data/lib/almirah/project/work_item_scheduler.rb +166 -0
  43. data/lib/almirah/project/working_calendar.rb +112 -0
  44. data/lib/almirah/project.rb +146 -9
  45. data/lib/almirah/project_configuration.rb +126 -29
  46. data/lib/almirah/project_template.rb +6 -6
  47. data/lib/almirah/project_utility.rb +3 -5
  48. data/lib/almirah/search/specifications_db.rb +2 -2
  49. data/lib/almirah/source_file_parser.rb +2 -3
  50. data/lib/almirah/templates/css/main.css +168 -1
  51. data/lib/almirah/templates/scripts/main.js +7 -2
  52. data/lib/almirah/templates/scripts/orama_search.js +17 -4
  53. data/lib/almirah.rb +1 -2
  54. metadata +12 -2
@@ -1,3 +1,8 @@
1
+ html {
2
+ /* Leave room for the fixed #top_nav so in-page anchor links
3
+ are not hidden behind it when navigating to them. */
4
+ scroll-padding-top: 40px;
5
+ }
1
6
  body {
2
7
  font-family: Verdana, sans-serif;
3
8
  font-size: 12px;
@@ -72,6 +77,9 @@ table.markdown_table td{
72
77
  table.markdown_table tr.current_status td {
73
78
  background-color: #ffffee;
74
79
  }
80
+ table.markdown_table td.scope_date {
81
+ white-space: nowrap;
82
+ }
75
83
  table.controlled{
76
84
  border: 1px solid #e4e4e4;
77
85
  border-collapse: collapse;
@@ -139,6 +147,163 @@ table.controlled td.item_text{
139
147
  max-width: 100%;
140
148
  max-height: 300px;
141
149
  }
150
+ .workitem_gantt {
151
+ --gantt-owner-width: 120px;
152
+ --gantt-day-width: 26px;
153
+ --gantt-month-h: 18px;
154
+ overflow: auto;
155
+ max-height: 420px;
156
+ margin-bottom: 16px;
157
+ border: 1px solid #e1e4e8;
158
+ }
159
+ .workitem_gantt .gantt_grid {
160
+ display: grid;
161
+ gap: 0;
162
+ width: max-content;
163
+ padding: 4px;
164
+ }
165
+ .workitem_gantt .gantt_corner,
166
+ .workitem_gantt .gantt_owner,
167
+ .workitem_gantt .gantt_owner_tracking,
168
+ .workitem_gantt .gantt_band_corner,
169
+ .workitem_gantt .gantt_buffer {
170
+ position: sticky;
171
+ left: 0;
172
+ background: #fff;
173
+ padding: 2px 8px;
174
+ font-weight: 600;
175
+ white-space: nowrap;
176
+ border-right: 1px solid #e1e4e8;
177
+ z-index: 2;
178
+ }
179
+ .workitem_gantt .gantt_corner {
180
+ top: 0;
181
+ border-bottom: 1px solid #bbb;
182
+ z-index: 3;
183
+ }
184
+ .workitem_gantt .gantt_month_head {
185
+ position: sticky;
186
+ top: 0;
187
+ background: #fff;
188
+ text-align: center;
189
+ font-size: 11px;
190
+ font-weight: 600;
191
+ color: #57606a;
192
+ border-bottom: 1px solid #e1e4e8;
193
+ border-left: 1px solid #d0d7de;
194
+ white-space: nowrap;
195
+ overflow: hidden;
196
+ z-index: 1;
197
+ }
198
+ .workitem_gantt .gantt_day_head {
199
+ position: sticky;
200
+ top: var(--gantt-month-h);
201
+ background: #fff;
202
+ text-align: center;
203
+ font-size: 11px;
204
+ color: #57606a;
205
+ border-bottom: 1px solid #bbb;
206
+ z-index: 1;
207
+ }
208
+ /* Weekday-holiday highlight in the Chart.js palette red (ADR-205 amendment):
209
+ the column carries the buffer bar's diagonal stripe texture, in red; the
210
+ header stays a solid tint so the day number reads. */
211
+ .workitem_gantt .gantt_day_head.gantt_nonworking { background: rgba(255, 99, 132, 0.22); color: #b23a52; }
212
+ .workitem_gantt .gantt_day_head.gantt_friday { background: rgb(225, 241, 250); }
213
+ .workitem_gantt .gantt_nonworking_col {
214
+ background: repeating-linear-gradient(45deg,
215
+ rgba(255, 99, 132, 0.20), rgba(255, 99, 132, 0.20) 5px,
216
+ rgba(255, 99, 132, 0.07) 5px, rgba(255, 99, 132, 0.07) 10px);
217
+ }
218
+ .workitem_gantt .gantt_friday_col { background: rgb(225, 241, 250); }
219
+ .workitem_gantt .gantt_bar,
220
+ .workitem_gantt .gantt_track_bar {
221
+ font-size: 11px;
222
+ line-height: 1.5;
223
+ margin: 1px;
224
+ padding: 1px 5px;
225
+ border-radius: 3px;
226
+ overflow: hidden;
227
+ white-space: nowrap;
228
+ text-overflow: ellipsis;
229
+ color: #fff;
230
+ cursor: default;
231
+ }
232
+ .workitem_gantt .gantt_todo { background: #9aa5b1; }
233
+ .workitem_gantt .gantt_inprogress { background: #ffffdd; color: #333; box-shadow: inset 0 0 0 1px #bbb; }
234
+ .workitem_gantt .gantt_done { background: #cfc; color: #57606a; box-shadow: inset 0 0 0 1px #e1e4e8; }
235
+ .workitem_gantt .gantt_blocked { box-shadow: inset 0 0 0 1px #bbb; }
236
+ .workitem_gantt .gantt_critical { outline: 1px solid #9a6700; outline-offset: -1px; } /* buffer-bar amber (dark) */
237
+ .workitem_gantt .gantt_release_band {
238
+ text-align: center;
239
+ font-size: 11px;
240
+ font-weight: 600;
241
+ color: #57606a;
242
+ background: #eef1f4;
243
+ border-bottom: 1px solid #e1e4e8;
244
+ border-left: 1px solid #d0d7de;
245
+ padding: 2px 4px;
246
+ white-space: nowrap;
247
+ overflow: hidden;
248
+ text-overflow: ellipsis;
249
+ }
250
+ .workitem_gantt .gantt_buffer { color: #9a6700; }
251
+ .critical_chain { margin-bottom: 16px; }
252
+ .critical_chain h2 { font-size: 1.1em; margin-bottom: 6px; }
253
+ .critical_chain .cc_group { margin: 8px 0; }
254
+ .critical_chain .cc_group h3 { font-size: 0.95em; margin: 4px 0; }
255
+ .critical_chain table.cc_chain { border-collapse: collapse; margin: 4px 0; }
256
+ .critical_chain table.cc_chain th,
257
+ .critical_chain table.cc_chain td { border: 1px solid #e1e4e8; padding: 2px 8px; text-align: left; font-size: 12px; }
258
+ .critical_chain .cc_buffer,
259
+ .critical_chain .cc_consumed,
260
+ .critical_chain .cc_projected,
261
+ .critical_chain .cc_finish { margin: 2px 0; font-size: 12px; }
262
+ .critical_chain .cc_consumed { color: #57606a; }
263
+ .critical_chain .cc_finish { font-weight: 600; }
264
+ .critical_chain .cc_unestimated { color: #9a6700; font-style: italic; font-size: 12px; }
265
+ .critical_chain .cc_group_body { display: flex; flex-wrap: wrap; gap: 24px; align-items: flex-start; }
266
+ .critical_chain .cc_plan { flex: 1 1 320px; }
267
+ .critical_chain .cc_fever { flex: 1 1 360px; max-width: 480px; height: 320px; position: relative; }
268
+ .critical_chain .cc_fever .fever_canvas { width: 100%; height: 100%; }
269
+ .workitem_gantt .gantt_buffer_bar {
270
+ font-size: 11px;
271
+ line-height: 1.5;
272
+ margin: 1px;
273
+ padding: 1px 5px;
274
+ border-radius: 3px;
275
+ text-align: center;
276
+ overflow: hidden;
277
+ white-space: nowrap;
278
+ text-overflow: ellipsis;
279
+ color: #9a6700;
280
+ background: repeating-linear-gradient(45deg, #ffe9b3, #ffe9b3 5px, #fff3d4 5px, #fff3d4 10px);
281
+ box-shadow: inset 0 0 0 1px #e0c068;
282
+ cursor: default;
283
+ }
284
+ .workitem_gantt .gantt_today { border-left: 2px solid #d33; z-index: 5; pointer-events: none; }
285
+ /* Plan-vs-actual tracking lanes (ADR-213): hidden until the toolbar toggle adds
286
+ show-actuals; a hidden lane's grid tracks collapse to zero height. */
287
+ .workitem_gantt .gantt_tracking_lane { display: none; }
288
+ .workitem_gantt.show-actuals .gantt_tracking_lane { display: block; }
289
+ .workitem_gantt .gantt_owner_tracking { font-weight: 400; font-style: italic; color: #57606a; }
290
+ .workitem_gantt .gantt_track_committed { background: #b0b6bd; }
291
+ .workitem_gantt .gantt_track_logged { background: #36a2eb; }
292
+ .gantt_container .gantt_toolbar {
293
+ display: flex;
294
+ justify-content: flex-end;
295
+ gap: 6px;
296
+ padding: 4px 0;
297
+ }
298
+ .gantt_container .gantt_actuals_toggle {
299
+ font-size: 12px;
300
+ padding: 3px 10px;
301
+ border: 1px solid #d0d7de;
302
+ border-radius: 4px;
303
+ background: #f6f8fa;
304
+ cursor: pointer;
305
+ }
306
+ .gantt_container .gantt_actuals_toggle:hover { background: #eef1f4; }
142
307
  table.controlled:not(.odd-even) tbody tr:nth-child(odd) { background-color:#f6f7f8; }
143
308
  table.controlled:not(.odd-even) tbody tr:nth-child(even) { background-color: #fff; }
144
309
  table.controlled:not(.odd-even) tbody tr:nth-child(odd):hover,
@@ -224,7 +389,9 @@ img{
224
389
  background: #EEEEEE;
225
390
  border: 1px solid #ddd;
226
391
  position: fixed;
227
- height: 100%; /* 100% Full-height */
392
+ top: 0; /* Anchor top and bottom so the pane spans exactly the */
393
+ bottom: 0; /* viewport; padding/border stay inside the border box so */
394
+ /* overflow-y scroll can reach the last items. */
228
395
  visibility: hidden;
229
396
  z-index: 1;
230
397
  overflow-y: auto;
@@ -25,7 +25,9 @@ function openNav() {
25
25
  document.getElementById("searchInput").style.display = "block";
26
26
  }
27
27
  }else{
28
- if (document.title.includes("Traceability Matrix:")){
28
+ var navPane = document.getElementById('nav_pane');
29
+ var hasNav = navPane && navPane.innerHTML.trim() !== "";
30
+ if (document.title.includes("Traceability Matrix:") || !hasNav){
29
31
  document.getElementById('document_tree_menu_item').style.display = "none";
30
32
  } else {
31
33
  document.getElementById('document_tree_menu_item').style.display = "block";
@@ -74,7 +76,10 @@ function openNav() {
74
76
 
75
77
  modal.style.display = "block";
76
78
  modalImg.src = clicked.src;
77
- captionText.innerHTML = clicked.alt;
79
+ // Author-supplied alt text: insert as plain text, never as HTML (ADR-188).
80
+ // Reading clicked.alt yields the decoded value, so innerHTML here would
81
+ // re-parse author markup as live DOM (DOM-based XSS).
82
+ captionText.textContent = clicked.alt;
78
83
  }
79
84
 
80
85
  function modal_close_OnClick(clicked){
@@ -1,6 +1,18 @@
1
1
  // Do search only on the Index Page
2
2
  import { create, search, insert } from 'https://unpkg.com/@orama/orama@3.1.18/dist/browser/index.js'
3
3
 
4
+ // Author-derived values are treated as untrusted (ADR-188). Every result field
5
+ // is inserted with safe DOM interfaces (createTextNode / property assignment) and
6
+ // never parsed as HTML, and any URL is admitted only if it is a relative reference
7
+ // or uses an allowed scheme; otherwise the link is rendered inert.
8
+ const ALLOWED_URL_SCHEMES = ['http', 'https', 'mailto'];
9
+ function safeUrl(url) {
10
+ const s = (url == null ? '' : String(url)).trim();
11
+ const m = s.match(/^([a-z][a-z0-9+.-]*):/i);
12
+ if (!m) return s; // relative path or anchor
13
+ return ALLOWED_URL_SCHEMES.includes(m[1].toLowerCase()) ? s : '#';
14
+ }
15
+
4
16
  // Create DB
5
17
  const db = await create({
6
18
  schema: {
@@ -87,19 +99,20 @@ async function search_onKeyUp(){
87
99
  let cell = document.createElement("td");
88
100
  let i = document.createElement("i");
89
101
  i.classList.add("fa","fa-file-text-o");
90
- i.style.backgroundColor = "#" + doc_color;
102
+ if (/^[0-9a-fA-F]{3,8}$/.test(doc_color)) {
103
+ i.style.backgroundColor = "#" + doc_color;
104
+ }
91
105
  cell.appendChild(i);
92
106
  let textnode = document.createTextNode("\xa0" + doc_title);
93
107
  cell.appendChild(textnode);
94
108
  row.appendChild(cell)
95
109
  cell = document.createElement("td");
96
110
 
97
- const a = document.createElement('a');
111
+ const a = document.createElement('a');
98
112
  const link = document.createTextNode(heading_text)
99
113
  a.appendChild(link);
100
114
  a.title = heading_text;
101
- a.href = heading_url;
102
- document.body.appendChild(a);
115
+ a.href = safeUrl(heading_url);
103
116
 
104
117
  cell.appendChild(a);
105
118
  row.appendChild(cell)
data/lib/almirah.rb CHANGED
@@ -54,8 +54,7 @@ class CLI < Thor
54
54
  end
55
55
 
56
56
  class Almirah
57
- attr_accessor :project
58
- attr_accessor :project_utility
57
+ attr_accessor :project, :project_utility
59
58
 
60
59
  def initialize(project_folder)
61
60
  config = ProjectConfiguration.new project_folder
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: Almirah
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Oleksandr Ivanov
@@ -73,28 +73,38 @@ files:
73
73
  - lib/almirah/doc_items/markdown_list.rb
74
74
  - lib/almirah/doc_items/markdown_table.rb
75
75
  - lib/almirah/doc_items/paragraph.rb
76
+ - lib/almirah/doc_items/scope_table.rb
76
77
  - lib/almirah/doc_items/source_code_paragraph.rb
77
78
  - lib/almirah/doc_items/text_line.rb
78
79
  - lib/almirah/doc_items/todo_block.rb
80
+ - lib/almirah/doc_items/work_item.rb
79
81
  - lib/almirah/doc_parser.rb
80
82
  - lib/almirah/doc_types/base_document.rb
81
83
  - lib/almirah/doc_types/coverage.rb
84
+ - lib/almirah/doc_types/critical_chain_page.rb
82
85
  - lib/almirah/doc_types/decision.rb
86
+ - lib/almirah/doc_types/decision_grouping.rb
83
87
  - lib/almirah/doc_types/decisions_overview.rb
84
88
  - lib/almirah/doc_types/implementation.rb
85
89
  - lib/almirah/doc_types/index.rb
86
90
  - lib/almirah/doc_types/persistent_document.rb
91
+ - lib/almirah/doc_types/planning_dates.rb
87
92
  - lib/almirah/doc_types/protocol.rb
88
93
  - lib/almirah/doc_types/source_file.rb
89
94
  - lib/almirah/doc_types/specification.rb
90
95
  - lib/almirah/doc_types/traceability.rb
91
96
  - lib/almirah/dom/doc_section.rb
92
97
  - lib/almirah/dom/document.rb
98
+ - lib/almirah/html_safe.rb
93
99
  - lib/almirah/link_registry.rb
94
100
  - lib/almirah/navigation_pane.rb
95
101
  - lib/almirah/project.rb
102
+ - lib/almirah/project/critical_chain.rb
96
103
  - lib/almirah/project/doc_linker.rb
104
+ - lib/almirah/project/fever_chart.rb
97
105
  - lib/almirah/project/project_data.rb
106
+ - lib/almirah/project/work_item_scheduler.rb
107
+ - lib/almirah/project/working_calendar.rb
98
108
  - lib/almirah/project_configuration.rb
99
109
  - lib/almirah/project_template.rb
100
110
  - lib/almirah/project_utility.rb
@@ -124,7 +134,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
124
134
  - !ruby/object:Gem::Version
125
135
  version: '0'
126
136
  requirements: []
127
- rubygems_version: 4.0.1
137
+ rubygems_version: 4.0.6
128
138
  specification_version: 4
129
139
  summary: Almirah
130
140
  test_files: []