hdoc-tools 0.8.13 → 0.8.15

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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Hornbill Docs
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/hdoc-build.js CHANGED
@@ -3,9 +3,7 @@
3
3
 
4
4
  const {
5
5
  createHash
6
- } = require('crypto'), {
7
- lutimesSync
8
- } = require('fs'),
6
+ } = require('crypto'),
9
7
  dree = require('dree'),
10
8
  fs = require('fs-extra'),
11
9
  mdfm = require('markdown-it-front-matter'),
@@ -27,22 +25,27 @@
27
25
  'doc_content',
28
26
  'doc_preview UNINDEXED',
29
27
  'doc_family_id'
30
- ];
28
+ ],
29
+ doc_header_template_path = path.join(__dirname, 'templates', 'doc-header.html');
31
30
 
32
- let conversion_attempted = 0,
31
+ let bc = {}, // Breadcrumbs map
32
+ book_read_time = 0,
33
+ built_file_hashes = [],
34
+ conversion_attempted = 0,
33
35
  conversion_success = 0,
34
36
  conversion_failed = 0,
37
+ doc_header_template = '',
38
+ doc_id = '',
39
+ git_token = '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', // Github fine-grained personal access token that has minimum read-only access to Hornbill Docs org repo content
40
+ hdocbook_config = {},
41
+ hdocbook_project,
35
42
  includes_found = 0,
36
43
  includes_success = 0,
37
44
  includes_failed = 0,
38
- book_read_time = 0,
39
- hdocbook_project,
40
- docId = '',
45
+ index_records = [],
41
46
  md_files = [],
42
47
  static_html_files = [],
43
- index_records = [],
44
- work_path_content = '',
45
- built_file_hashes = [];
48
+ work_path_content = '';
46
49
 
47
50
  const transform_static_html = function (file_path) {
48
51
  if (fs.existsSync(file_path.path)) {
@@ -205,7 +208,9 @@
205
208
  let fm_content = frontmatter_content.split(/\r?\n/);
206
209
 
207
210
  let fm_contains_title = false,
208
- fm_contains_reading_time = false;
211
+ fm_contains_reading_time = false,
212
+ doc_title = '',
213
+ doc_type = 'Article';
209
214
 
210
215
  if (fm_content.length >= 0) {
211
216
  fm_content.forEach(function (fm_prop) {
@@ -218,6 +223,11 @@
218
223
 
219
224
  if (fm_property[0].trim() === 'title') {
220
225
  fm_contains_title = true;
226
+ doc_title = fm_property[1].trim();
227
+ }
228
+ if (fm_property[0].trim() === 'type') {
229
+ fm_contains_type = true;
230
+ doc_type = fm_property[1].trim();
221
231
  }
222
232
  if (fm_property[0].trim() === 'reading-time') {
223
233
  book_read_time += parseInt(fm_property[1].trim(), 10);
@@ -227,6 +237,12 @@
227
237
  });
228
238
  }
229
239
 
240
+ // Add doc type
241
+ fm_headers.push({
242
+ id: 'type',
243
+ value: doc_type
244
+ });
245
+
230
246
  // Does frontmatter tag contain a title property
231
247
  if (!fm_contains_title) {
232
248
  // Frontmatter tags don't contain a title property - go pull the first one from the html heading tags
@@ -238,12 +254,16 @@
238
254
  id: 'title',
239
255
  value: html_heading[0].children[0].data.trim()
240
256
  });
257
+ doc_title = html_heading[0].children[0].data.trim();
241
258
  } else {
242
259
  // No header tag, no frontmatter title, output a warning
243
260
  console.log(`No frontmatter title property, or h1, h2 or h3 header tags detected in ${file_path}`);
244
261
  }
245
262
  }
246
263
 
264
+ // Remove the first <h1>title</h1> from the HTML as we'll ass that in the document header
265
+ html_txt = html_txt.replace(/(<h1.*?>)\s*.*\s*(.*<\/h1>)/, '');
266
+
247
267
  // Does frontmatter tag contain a reading-time property
248
268
  if (!fm_contains_reading_time) {
249
269
  const read_time_mins = hdoc.get_html_read_time(html_txt);
@@ -254,15 +274,51 @@
254
274
  });
255
275
  }
256
276
 
257
- // Add frontmatter tags as comment to front of HTML
277
+ // Get contributor data from Github, if exists
278
+ if (hdocbook_config.publicSource && hdocbook_config.publicSource !== '') {
279
+ const github_paths = hdoc.get_github_api_path(hdocbook_config.publicSource, file_path.relativePath);
280
+ const contributors = hdoc.get_github_contributors(github_paths.api_path, git_token);
281
+ if (!contributors.success) {
282
+ console.log(`Error retrieving contributors from Github: ${contributors.error}`);
283
+ } else {
284
+ fm_headers.push({
285
+ id: 'contributor-count',
286
+ value: contributors.contributor_count
287
+ });
288
+ fm_headers.push({
289
+ id: 'last-commit',
290
+ value: contributors.last_commit_date
291
+ });
292
+ }
293
+ fm_headers.push({
294
+ id: 'edit-path',
295
+ value: github_paths.edit_path
296
+ });
297
+ const target_file = file_path.path.replace(path.extname(file_path.path), '._info.json');
298
+ delete contributors.success;
299
+ delete contributors.error;
300
+ contributors.editPath = github_paths.edit_path;
301
+ try {
302
+ fs.writeFileSync(target_file, JSON.stringify(contributors, null, 2));
303
+ } catch (err) {
304
+ console.log('Error writing:', target_file, '\n', err);
305
+ }
306
+ }
307
+
308
+ // Build doc header from template and frontmatter tags
309
+ const doc_header = process_doc_header(fm_headers, file_path.relativePath);
310
+
311
+ // Add frontmatter tags as comment to front of HTML
312
+
313
+ let fm_header = '<!--[[FRONTMATTER\n';
258
314
  if (fm_headers.length > 0) {
259
- let fm_header = '<!--[[FRONTMATTER\n';
260
315
  for (let i = 0; i < fm_headers.length; i++) {
261
316
  fm_header += `${fm_headers[i].id}: ${fm_headers[i].value}\n`;
262
317
  }
263
- fm_header += ']]-->';
264
- html_txt = `${fm_header}\n${html_txt}`;
265
318
  }
319
+ fm_header += ']]-->';
320
+
321
+ html_txt = `${fm_header}\n${doc_header}\n${html_txt}`;
266
322
 
267
323
  // Save HTML into HTML file
268
324
  const target_file = file_path.path.replace(path.extname(file_path.path), '.html');
@@ -292,6 +348,46 @@
292
348
  return false;
293
349
  };
294
350
 
351
+ const process_doc_header = function (fm_headers, doc_path) {
352
+ let wip_doc_header = doc_header_template;
353
+
354
+ // Process fm_headers properties first
355
+ for (let i = 0; i < fm_headers.length; i++) {
356
+ switch (fm_headers[i].id) {
357
+ case 'title':
358
+ wip_doc_header = wip_doc_header.replaceAll('{{title}}', fm_headers[i].value);
359
+ break;
360
+ case 'reading-time':
361
+ wip_doc_header = wip_doc_header.replaceAll('{{reading-time}}', fm_headers[i].value);
362
+ break;
363
+ case 'contributor-count':
364
+ wip_doc_header = wip_doc_header.replaceAll('{{contributor-count}}', fm_headers[i].value);
365
+ break;
366
+ case 'type':
367
+ wip_doc_header = wip_doc_header.replaceAll('{{doc-type}}', fm_headers[i].value);
368
+ break;
369
+ case 'edit-path':
370
+ wip_doc_header = wip_doc_header.replaceAll('{{edit-url}}', fm_headers[i].value);
371
+ break;
372
+ case 'last-commit':
373
+ const last_commit_date = new Date(fm_headers[i].value).toDateString();
374
+ wip_doc_header = wip_doc_header.replaceAll('{{last-update}}', last_commit_date);
375
+ break;
376
+ }
377
+ }
378
+
379
+ // Now sort out breadcrumbs
380
+ const logical_path = doc_path.replace(path.extname(doc_path), '');
381
+ const bc_for_path = bc[logical_path];
382
+ let bc_tags = '\n';
383
+ for (let i = 0; i < bc_for_path.length - 1; i++) {
384
+ bc_tags += `\t\t\t\t<li class="mt-0 nav-bar-item"><a href="${bc_for_path[i].link}" class="ps-0 pe-0 text-decoration-none">${bc_for_path[i].text}</a></li>\n`;
385
+ }
386
+ bc_tags += '\t\t\t';
387
+ wip_doc_header = wip_doc_header.replaceAll('{{breadcrumbs}}', bc_tags);
388
+ return wip_doc_header;
389
+ };
390
+
295
391
  // File callback for build scan
296
392
  const build_file_callback = function (element) {
297
393
  if (element.extension === 'md') {
@@ -330,7 +426,114 @@
330
426
  sorted: true
331
427
  };
332
428
 
333
- exports.run = function (source_path, verbose) {
429
+ const build_breadcrumbs = function (nav_items) {
430
+ console.log('Processing navigation breadcrumbs...\n');
431
+ // Steve - need to rework this to use recursion, as it's hard-coded for a maximum of 4 levels deep right now
432
+ for (let a = 0; a < nav_items.length; a++) {
433
+ const nav_a = nav_items[a];
434
+ const parent_a = nav_a.text;
435
+ if (nav_a.link) {
436
+ if (nav_a.link.includes('\\')) {
437
+ console.log(`Navigation items should not contain backslashes: ${nav_a.link}`);
438
+ process.exit(1);
439
+ }
440
+ bc[nav_a.link] = [{
441
+ text: nav_a.text,
442
+ link: nav_a.link
443
+ }];
444
+ }
445
+ if (nav_a.items) {
446
+ for (let b = 0; b < nav_a.items.length; b++) {
447
+ const nav_b = nav_a.items[b];
448
+ const parent_b = nav_b.text;
449
+ if (nav_b.link) {
450
+ if (nav_b.link.includes('\\')) {
451
+ console.log(`Navigation items should not contain backslashes: ${nav_b.link}`);
452
+ process.exit(1);
453
+ }
454
+ if (bc[nav_a.link]) {
455
+ bc[nav_b.link] = bc[nav_a.link];
456
+ } else {
457
+ bc[nav_b.link] = [{
458
+ text: parent_a,
459
+ link: nav_a.items[0].link
460
+ }];
461
+ }
462
+ bc[nav_b.link].push({
463
+ text: nav_b.text,
464
+ link: nav_b.link
465
+ });
466
+ }
467
+
468
+ if (nav_b.items) {
469
+ for (let c = 0; c < nav_b.items.length; c++) {
470
+ const nav_c = nav_b.items[c];
471
+ const parent_c = nav_c.text;
472
+ if (nav_c.link) {
473
+ if (nav_c.link.includes('\\')) {
474
+ console.log(`Navigation items should not contain backslashes: ${nav_c.link}`);
475
+ process.exit(1);
476
+ }
477
+ if (bc[nav_b.link]) {
478
+ bc[nav_c.link] = bc[nav_b.link];
479
+ } else {
480
+ bc[nav_c.link] = [{
481
+ text: parent_a,
482
+ link: nav_a.items[0].link
483
+ }];
484
+ bc[nav_c.link].push({
485
+ text: parent_b,
486
+ link: nav_b.items[0].link
487
+ });
488
+ }
489
+ bc[nav_c.link].push({
490
+ text: nav_c.text,
491
+ link: nav_c.link
492
+ });
493
+ }
494
+
495
+ if (nav_c.items) {
496
+ for (let d = 0; d < nav_c.items.length; d++) {
497
+ const nav_d = nav_c.items[d];
498
+ if (nav_d.link) {
499
+ if (bc[nav_c.link]) {
500
+ bc[nav_d.link] = bc[nav_c.link];
501
+ } else {
502
+ bc[nav_d.link] = [{
503
+ text: parent_a,
504
+ link: nav_a.items[0].link
505
+ }];
506
+ bc[nav_d.link].push({
507
+ text: parent_b,
508
+ link: nav_b.items[0].link
509
+ });
510
+ bc[nav_d.link].push({
511
+ text: parent_c,
512
+ link: nav_c.items[0].link
513
+ });
514
+ }
515
+ bc[nav_d.link].push({
516
+ text: nav_d.text,
517
+ link: nav_d.link
518
+ });
519
+ }
520
+ }
521
+
522
+ }
523
+ }
524
+ }
525
+ }
526
+ }
527
+
528
+ }
529
+ //console.log(JSON.stringify(bc, null, 2));
530
+ };
531
+
532
+ exports.run = function (source_path, verbose, github_api_token) {
533
+ if (github_api_token !== '') {
534
+ git_token = github_api_token;
535
+ }
536
+
334
537
  // GERRY: The purpose of this function is to create a zip file containing the hdocbook content,
335
538
  // * Create a _work folder
336
539
  // * copy the hdocbook content to the work folder
@@ -348,20 +551,19 @@
348
551
  // use the docId to get the book config
349
552
  const hdocbook_project_config_path = path.join(source_path, 'hdocbook-project.json');
350
553
  hdocbook_project = require(hdocbook_project_config_path);
554
+ doc_id = hdocbook_project.docId;
351
555
 
352
- docId = hdocbook_project.docId;
353
-
354
- const book_path = path.join(source_path, docId),
556
+ const book_path = path.join(source_path, doc_id),
355
557
  hdocbook_path = path.join(book_path, 'hdocbook.json'),
356
- hdocbook_config = require(hdocbook_path),
357
558
  work_path = path.join(source_path, '_work'),
358
- work_hdocbook_path = path.join(work_path, docId, 'hdocbook.json');
559
+ work_hdocbook_path = path.join(work_path, doc_id, 'hdocbook.json');
359
560
 
360
- console.log(`Building: ${docId} v${hdocbook_config.version}...\n`);
561
+ hdocbook_config = require(hdocbook_path);
562
+
563
+ console.log(`Building: ${doc_id} v${hdocbook_config.version}...\n`);
361
564
 
362
-
363
- work_path_content = path.join(work_path, docId);
364
565
  // Make _work folder to copy everything into
566
+ work_path_content = path.join(work_path, doc_id);
365
567
  if (fs.existsSync(work_path)) {
366
568
  fs.rmSync(work_path, {
367
569
  recursive: true,
@@ -370,14 +572,24 @@
370
572
  }
371
573
  fs.mkdirSync(work_path);
372
574
 
373
- // Copy files from book into _work-docId folder
575
+ // Copy files from book into _work-doc_id folder
374
576
  try {
375
- fs.copySync(path.join(source_path, docId), work_path_content);
577
+ fs.copySync(path.join(source_path, doc_id), work_path_content);
376
578
  } catch (e) {
377
579
  console.error('Error copying from source_path:\n', e);
378
580
  process.exit(1);
379
581
  }
380
582
 
583
+ // Load document header template
584
+ try {
585
+ doc_header_template = fs.readFileSync(doc_header_template_path, 'utf8');
586
+ } catch (err) {
587
+ console.log(`Error reading document header template: ${err}`);
588
+ process.exit(1);
589
+ }
590
+
591
+ build_breadcrumbs(hdocbook_config.navigation.items);
592
+
381
593
  // Get a list of MD files in work_path
382
594
  dree.scan(work_path, dreeOptions, build_file_callback);
383
595
 
@@ -391,25 +603,23 @@
391
603
  transform_static_html(static_html_file);
392
604
  });
393
605
 
394
-
395
606
  console.log(` MD files found: ${conversion_attempted}`);
396
607
  console.log(`Successfully converted to HTML: ${conversion_success}`);
397
608
  console.log(` Failed to convert: ${conversion_failed}\n`);
398
609
  console.log(` Includes Found: ${includes_found}`);
399
610
  console.log(` Includes Success: ${includes_success}`);
400
611
  console.log(` Includes Failed: ${includes_failed}\n`);
401
-
402
612
  console.log(` Static HTML Files Found: ${static_html_files.length}\n`);
403
613
 
404
614
  // Validate content
405
- const validation_success = validate.run(work_path, docId, verbose);
615
+ const validation_success = validate.run(work_path, doc_id, verbose);
406
616
  if (!validation_success) {
407
617
  process.exit(1);
408
618
  }
409
619
 
410
620
  // Now build the index
411
621
  console.log('Performing SQlite index creation...');
412
- let db_name = path.join(work_path, docId, 'index.db');
622
+ let db_name = path.join(work_path, doc_id, 'index.db');
413
623
  const db = new Database(db_name);
414
624
  db.pragma('encoding="UTF-8"');
415
625
  console.log(`\nDatabase created: ${db_name}`);
@@ -425,11 +635,11 @@
425
635
  if (!hdocbook_config.tags) hdocbook_config.tags = [];
426
636
  let index_success_count = 0;
427
637
  for (let i = 0; i < index_records.length; i++) {
428
- let index_path_name = index_records[i].relative_path.replace('\\', '/').replace(`${hdocbook_config.docId}/`, '');
638
+ let index_path_name = index_records[i].relative_path.replace('\\', '/').replace(`${doc_id}/`, '');
429
639
  index_path_name = index_path_name.replace(path.extname(index_records[i].relative_path), '');
430
640
  const index_vals = [
431
641
  index_path_name,
432
- hdocbook_config.docId,
642
+ doc_id,
433
643
  hdocbook_config.audience.join(','),
434
644
  hdocbook_config.tags.join(','),
435
645
  index_records[i].index_html.fm_props.title,
@@ -437,14 +647,14 @@
437
647
  index_records[i].index_html.preview,
438
648
  hdocbook_config.productFamily
439
649
  ];
440
- const index_response = hdoc_index.insert_record(db, table_name, index_cols, index_vals, hdocbook_config.docId, index_records[i].index_html.fm_props.title);
650
+ const index_response = hdoc_index.insert_record(db, table_name, index_cols, index_vals, doc_id, index_records[i].index_html.fm_props.title);
441
651
  if (!index_response.success) {
442
- console.log(`Index record creation failed - ${hdocbook_config.docId}/${index_records[i].index_html.fm_props.title}: ${index_response.error}`);
652
+ console.log(`Index record creation failed - ${doc_id}/${index_records[i].index_html.fm_props.title}: ${index_response.error}`);
443
653
  continue;
444
654
  }
445
655
  index_success_count++;
446
656
  if (verbose) {
447
- console.log(`Inserted index record ${index_response.row_id}: ${hdocbook_config.docId} - ${index_records[i].index_html.fm_props.title}`);
657
+ console.log(`Inserted index record ${index_response.row_id}: ${doc_id} - ${index_records[i].index_html.fm_props.title}`);
448
658
  }
449
659
  }
450
660
  console.log(`\nIndex Build Complete: ${index_success_count} records created.`);
@@ -483,7 +693,7 @@
483
693
  }
484
694
 
485
695
  try {
486
- const zip_path = path.join(work_path, docId + '.zip');
696
+ const zip_path = path.join(work_path, doc_id + '.zip');
487
697
  zipper.sync.zip(work_path_content).compress().save(zip_path);
488
698
  console.log(`\nZIP Creation Success: ${zip_path}\n`);
489
699
  console.log('Build Complete\n');
package/hdoc-module.js CHANGED
@@ -213,4 +213,73 @@
213
213
  if (read_time === 0) read_time = 1;
214
214
  return read_time;
215
215
  };
216
+
217
+ exports.get_github_api_path = function(repo, relative_path) {
218
+ repo = repo.endsWith('/') ? repo.slice(0, -1) : repo;
219
+ let github_paths = {};
220
+ github_paths.api_path = repo.replace('https://github.com/', 'https://api.github.com/repos/');
221
+ github_paths.api_path += '/commits?path=/' + relative_path.replace('\\\\', '/').replace('\\', '/');
222
+ github_paths.edit_path = repo + '/blob/main/' + relative_path.replace('\\\\', '/').replace('\\', '/');
223
+ return github_paths;
224
+ };
225
+
226
+ exports.get_github_contributors = function(github_url, github_api_token) {
227
+ let response = {
228
+ success: false,
229
+ error: '',
230
+ contributors: [],
231
+ contributor_count: 0,
232
+ last_commit_date:''
233
+ };
234
+
235
+ let contributors = {};
236
+
237
+ let request_options = {
238
+ headers: {
239
+ 'User-Agent': 'HornbillDocsBuild',
240
+ 'Cache-Control': 'no-cache',
241
+ 'Host': 'api.github.com',
242
+ 'Accept': 'application/json'
243
+ }
244
+ };
245
+ if (github_api_token !== '') {
246
+ request_options.headers.authorization = `Bearer ${github_api_token}`;
247
+ }
248
+ const github_response = request('GET', github_url, request_options);
249
+
250
+ if (github_response.statusCode === 200) {
251
+ response.success = true;
252
+ let commits = github_response.getBody('UTF8');
253
+ commits = JSON.parse(commits);
254
+ commits.forEach(function(commit){
255
+ if (commit.committer.type.toLowerCase() === 'user' && commit.committer.login.toLowerCase() !== 'web-flow') {
256
+ if (!contributors[commit.committer.id]) {
257
+ response.contributor_count++;
258
+ contributors[commit.committer.id] = {
259
+ login: commit.committer.login,
260
+ avatar_url: commit.committer.avatar_url,
261
+ html_url: commit.committer.html_url,
262
+ name: commit.commit.committer.name
263
+ };
264
+ }
265
+ if (response.last_commit_date !== '') {
266
+ const new_commit_date = new Date(commit.commit.committer.date);
267
+ const exist_commit_date = new Date(response.last_commit_date);
268
+ if (new_commit_date > exist_commit_date) response.last_commit_date = commit.commit.committer.date;
269
+ } else {
270
+ response.last_commit_date = commit.commit.committer.date;
271
+ }
272
+ }
273
+ });
274
+ for (const key in contributors) {
275
+ if (contributors.hasOwnProperty(key)) {
276
+ response.contributors.push(contributors[key]);
277
+ }
278
+ }
279
+ } else {
280
+ response.error = `Unexpected Status ${github_response.statusCode}. ${JSON.parse(github_response.body.toString('utf8')).message}`;
281
+ }
282
+ return response;
283
+ };
284
+
216
285
  })();
package/hdoc.js CHANGED
@@ -25,7 +25,7 @@
25
25
  // Default source path to working directory
26
26
  let source_path = process.cwd();
27
27
  let ui_path = path.join(__dirname, 'ui');
28
-
28
+ let git_token = '';
29
29
  let command = '', // Our command to run
30
30
  verbose = false;
31
31
 
@@ -50,6 +50,11 @@
50
50
  if (x < process.argv.length) {
51
51
  ui_path = process.argv[x];
52
52
  }
53
+ } else if (process.argv[x] == '-git-token') {
54
+ x++;
55
+ if (x < process.argv.length) {
56
+ git_token = process.argv[x];
57
+ }
53
58
  }
54
59
 
55
60
  if (process.argv[x] === '-v') {
@@ -66,7 +71,7 @@
66
71
  server.run(ui_path, source_path);
67
72
  } else if (command == 'build') {
68
73
  const builder = require(path.join(__dirname, 'hdoc-build.js'));
69
- builder.run(source_path, verbose);
74
+ builder.run(source_path, verbose, git_token);
70
75
  } else if (command == 'stats') {
71
76
  const stats = require(path.join(__dirname, 'hdoc-stats.js'));
72
77
  stats.run(ui_path, source_path, verbose);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hdoc-tools",
3
- "version": "0.8.13",
3
+ "version": "0.8.15",
4
4
  "description": "Hornbill HDocBook Development Support Tool",
5
5
  "main": "hdoc.js",
6
6
  "bin": {
@@ -0,0 +1,30 @@
1
+ <div class="document-header">
2
+ <div class="hb-container-horizontal">
3
+ <div class="hb-center-v hb-container-expand">
4
+ <ul class="ps-0 nav-bar-nav-list noeffects after-fslash overflow-ellipsis">{{breadcrumbs}}</ul>
5
+ </div>
6
+ <div class="toolbar-right hb-center-v">
7
+ <a target="_blank" class="toolbar-action" href="{{edit-url}}">
8
+ <i class="bi bi-pencil"></i>
9
+ </a>
10
+ <a class="toolbar-action documentation-menu-popper">
11
+ <i class="bi bi-three-dots-vertical"></i>
12
+ </a>
13
+ </div>
14
+ </div>
15
+
16
+ <h1>{{title}}</h1>
17
+
18
+ <div class="hb-container-horizontal">
19
+ <div class="hb-center-v hb-container-expand">
20
+ <ul class="ps-0 nav-bar-nav-list noeffects after-bullets overflow-ellipsis text-light-2">
21
+ <li class="ps-0 mt-0 nav-bar-item">{{doc-type}}</li>
22
+ <li class="ps-0 mt-0 nav-bar-item">{{last-update}}</li>
23
+ <li class="ps-0 mt-0 nav-bar-item">{{reading-time}} minutes to read</li>
24
+ <li class="ps-0 mt-0 nav-bar-item">
25
+ <a class="link c-pointer" data-bs-toggle="modal" data-bs-target="#contributersModal">{{contributor-count}} contributors</a>
26
+ </li>
27
+ </ul>
28
+ </div>
29
+ </div>
30
+ </div>
@@ -112,7 +112,7 @@ samp {
112
112
 
113
113
  img,
114
114
  svg {
115
- display: inline;
115
+ display: inline-block;
116
116
  }
117
117
 
118
118
  video,