-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(web app): add ability to navigate to particular sections
- implement complete content taxonomy - implement first draft of table-of-contents logic - improve code organization
- Loading branch information
1 parent
712e45f
commit 0e92881
Showing
98 changed files
with
25,958 additions
and
9,403 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,39 +1,56 @@ | ||
# Tasks | ||
|
||
- add GitHooks for linting and formatting on commits [to just `master`, if possible] | ||
- render all content | ||
- citation markers | ||
- opening content | ||
- what else? | ||
- implement content loading | ||
- cross-reference navigation | ||
- chapter > chapter navigation | ||
- routing | ||
- to in-page anchor tags | ||
|
||
- styling | ||
- [ ] try converting JSON artifacts to YAML | ||
- [ ] upgrade Fresh | ||
- [ ] move import-map specs into `deno.json` | ||
- [ ] use Twind v1: https://fresh.deno.dev/docs/examples/using-twind-v1 | ||
- [ ] document link regarding Radix colors and their other tools | ||
|
||
- [ ] rebuild the table-of-contents generation algorithm: | ||
- [ ] the algorithm: | ||
- [ ] for the four parts: | ||
- [ ] all populated `openingContent` arrays are an entry (referenced by the parent) | ||
- [ ] all `mainContent` arrays are split by `InBrief` content; entries are created for each sub-array (referenced | ||
by the first element in the sub-array) | ||
- [ ] write tests to verify that all content is accessible via the generated table-of-contents | ||
- [ ] determine if the semantic-path-to-path-id map needs refactoring | ||
|
||
- [ ] investigate "TODO" note above `utils::hasChildContent()` | ||
- [ ] investigate `utils::getOpeningAndMainContent()`: should the return type be `ContentBase` instead of `T`? | ||
|
||
- [ ] render all content | ||
- [ ] re-do the rendering structures (reorganize components, etc.) | ||
- [ ] opening content | ||
- [ ] citation markers | ||
- [ ] is there anything else? | ||
- [ ] implement content loading | ||
- [ ] cross-reference navigation | ||
- [ ] chapter > chapter navigation | ||
- [ ] routing | ||
- [ ] to in-page anchor tags | ||
|
||
- [ ] styling | ||
|
||
## Unprioritized | ||
|
||
- implement table-of-contents navigation | ||
- implement hierarchical navigation | ||
- implement historical navigation | ||
- implement search | ||
- implement index | ||
- implement glossary | ||
- implement "copy" buttons (click a button to copy the entire text of a paragraph, quote, etc.) | ||
- light/dark/high-contrast mode toggle | ||
- dark mode: try to avoid the "window blending" (cannot tell where the browser window starts and another application | ||
window begins) | ||
- consider using the following colors: | ||
- #E86D82 (red-pink) | ||
- [ ] implement hierarchical navigation | ||
- [ ] implement historical navigation | ||
- [ ] implement search | ||
- [ ] implement index | ||
- [ ] implement glossary | ||
- [ ] implement "copy" buttons (click a button to copy the entire text of a paragraph, quote, etc.) | ||
- [ ] light/dark/high-contrast mode toggle | ||
- [ ] dark mode: try to avoid the "window blending" problem (cannot tell where the browser window starts and another | ||
application window begins) | ||
- [ ] consider using the following colors: | ||
- [ ] #E86D82 (red-pink) | ||
|
||
# Possible features | ||
|
||
- the ability to ask a question in natural language (via text or mic), e.g. "What happens in the sacrament of | ||
Confirmation?" | ||
- note-taking and highlighting | ||
- permanent and temporary storage (easily toggleable) | ||
- narration | ||
- recordered audio (better than a screen reader) | ||
- text is highlighted to follow along (toggleable) | ||
- [ ] the ability to ask a question in natural language (via text or mic), e.g. "What happens in the sacrament of | ||
Confirmation?" | ||
- [ ] note-taking and highlighting | ||
- [ ] permanent and temporary storage (easily toggleable) | ||
- [ ] narration | ||
- [ ] recordered audio (better than a screen reader) | ||
- [ ] text is highlighted to follow along (toggleable) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,5 +3,8 @@ | |
{ | ||
"path": "." | ||
} | ||
] | ||
} | ||
], | ||
"settings": { | ||
"deno.enable": true | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
import { build as buildPathMap } from './path-map.ts'; | ||
import { build as buildTableOfContents } from './table-of-contents.ts'; | ||
|
||
buildArtifacts(); | ||
|
||
function buildArtifacts(): void { | ||
console.log('\nBuilding artifacts ...'); | ||
|
||
console.log('\ttable-of-contents ...'); | ||
const tableOfContents = buildTableOfContents(); | ||
writeJson(tableOfContents, 'table-of-contents'); | ||
|
||
console.log('\tsemantic-path to path-id map ...'); | ||
const pathMap = buildPathMap(tableOfContents); | ||
writeJson(pathMap, 'semantic-path-to-path-id'); | ||
} | ||
|
||
function writeJson(object: Record<string, unknown>, filename: string): void { | ||
Deno.writeTextFileSync( | ||
`catechism/artifacts/${filename}.json`, | ||
JSON.stringify(object, undefined, ' ') + '\n', | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
import { PathMap, TableOfContentsEntry, TableOfContentsType } from '../source/types/types.ts'; | ||
|
||
export function build(tableOfContents: TableOfContentsType): PathMap { | ||
const pathMaps = [ | ||
tableOfContents.prologue, | ||
...tableOfContents.parts, | ||
].flatMap((entry) => entryToPathMaps(entry)); | ||
|
||
return pathMaps.reduce((previous, current) => ( | ||
{ | ||
...previous, | ||
...current, | ||
} | ||
)); | ||
} | ||
|
||
function entryToPathMaps(entry: TableOfContentsEntry): Array<PathMap> { | ||
return [ | ||
getPathMap(entry), | ||
...entry.children.flatMap((childEntry) => entryToPathMaps(childEntry)), | ||
]; | ||
} | ||
|
||
function getPathMap(entry: TableOfContentsEntry): PathMap { | ||
return { | ||
[entry.semanticPath]: entry.pathID, | ||
}; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,162 @@ | ||
import { Catechism } from '../source/catechism.ts'; | ||
import { ArticleParagraph } from '../source/types/article-paragraph.ts'; | ||
import { Subarticle } from '../source/types/subarticle.ts'; | ||
import { | ||
Article, | ||
buildSemanticPath, | ||
Chapter, | ||
Content, | ||
ContentBase, | ||
ContentContainer, | ||
Paragraph, | ||
Part, | ||
Section, | ||
SemanticPathSource, | ||
TableOfContentsEntry, | ||
TableOfContentsType, | ||
} from '../source/types/types.ts'; | ||
import { getParagraphs, hasMainContent, hasOpeningContent } from '../utils.ts'; | ||
import { ParagraphGroup } from '../source/types/paragraph-group.ts'; | ||
|
||
//#region builders | ||
export function build(): TableOfContentsType { | ||
return { | ||
prologue: buildEntry(Catechism.prologue, [], true), | ||
parts: Catechism.parts.map((part) => buildEntry(part, [])), | ||
}; | ||
} | ||
|
||
function buildEntry<T extends ContentBase | ContentBase & ContentContainer>( | ||
content: T, | ||
ancestors: Array<SemanticPathSource>, | ||
ignoreChildren = false, | ||
): TableOfContentsEntry { | ||
const firstParagraphNumber = getFirstParagraphNumber(content); | ||
if (typeof firstParagraphNumber !== 'number') { | ||
throw Error( | ||
`A paragraph could not be found for the given content: ${content.contentType}, pathID: ${content.pathID}`, | ||
); | ||
} | ||
|
||
const semanticPathSource = getSemanticPathSource(content); | ||
|
||
return { | ||
contentType: content.contentType, | ||
title: getTitle(content), | ||
pathID: content.pathID, | ||
semanticPath: buildSemanticPath(semanticPathSource, ancestors), | ||
firstParagraphNumber, | ||
children: ignoreChildren ? [] : buildChildEntries(content, [semanticPathSource, ...ancestors]), | ||
}; | ||
} | ||
|
||
function buildChildEntries<T extends ContentBase | ContentBase & ContentContainer>( | ||
content: T, | ||
ancestors: Array<SemanticPathSource>, | ||
): Array<TableOfContentsEntry> { | ||
const childEntries: Array<TableOfContentsEntry> = []; | ||
|
||
const hasPopulatedOpeningContent = hasOpeningContent(content) && | ||
(content as ContentContainer).openingContent.length > 0; | ||
|
||
if (hasPopulatedOpeningContent) { | ||
// TODO: Implement | ||
/*/ | ||
const openingContentRoot = (content as ContentContainer).openingContent[0]; | ||
// TODO: Which is correct? (Should `openingContentRoot` or `content` be used?) | ||
const openingContentEntry: TableOfContentsEntry = { | ||
contentType: openingContentRoot.contentType, | ||
title: 'Opening Content', | ||
pathID: openingContentRoot.pathID, | ||
semanticPath: TODO, | ||
firstParagraphNumber: TODO, | ||
children: [] | ||
}; | ||
const openingContentEntry: TableOfContentsEntry = { | ||
contentType: content.contentType, | ||
title: 'Opening Content', | ||
pathID: content.pathID, | ||
semanticPath: TODO, | ||
firstParagraphNumber: TODO, | ||
children: [] | ||
}; | ||
childEntries.push(openingContentEntry); | ||
/*/ | ||
} | ||
|
||
const mainContentEntries = hasMainContent(content) | ||
? (content as ContentContainer).mainContent | ||
.filter((content) => includeInTableOfContents(content)) | ||
.map((child) => buildEntry(child, ancestors)) | ||
: []; | ||
|
||
return childEntries.concat(mainContentEntries); | ||
} | ||
//#endregion | ||
|
||
//#region helpers | ||
/** | ||
* @returns `true` if the content should be included in the Table of Contents, and `false` otherwise | ||
*/ | ||
function includeInTableOfContents<T extends ContentBase>(content: T): boolean { | ||
return Content.PROLOGUE === content.contentType || | ||
Content.PART === content.contentType || | ||
Content.SECTION === content.contentType || | ||
Content.CHAPTER === content.contentType || | ||
Content.ARTICLE === content.contentType || | ||
Content.ARTICLE_PARAGRAPH === content.contentType || | ||
Content.SUB_ARTICLE === content.contentType || | ||
Content.IN_BRIEF === content.contentType; | ||
} | ||
|
||
function getTitle(content: ContentBase): string { | ||
return `${Content[content.contentType]} ${content.pathID}`; | ||
} | ||
|
||
function getSemanticPathSource<T extends ContentBase>( | ||
content: T, | ||
): SemanticPathSource { | ||
return { | ||
content: content.contentType, | ||
number: getNumber(content), | ||
}; | ||
} | ||
|
||
function getNumber<T extends ContentBase>(content: T): number | null { | ||
if (Content.PART === content.contentType) { | ||
return (content as unknown as Part).partNumber; | ||
} else if (Content.SECTION === content.contentType) { | ||
return (content as unknown as Section).sectionNumber; | ||
} else if (Content.CHAPTER === content.contentType) { | ||
return (content as unknown as Chapter).chapterNumber; | ||
} else if (Content.ARTICLE === content.contentType) { | ||
return (content as unknown as Article).articleNumber; | ||
} else if (Content.ARTICLE_PARAGRAPH === content.contentType) { | ||
return (content as unknown as ArticleParagraph).articleParagraphNumber; | ||
} else if (Content.SUB_ARTICLE === content.contentType) { | ||
return (content as unknown as Subarticle).subarticleNumber; | ||
} else if (Content.PARAGRAPH_GROUP === content.contentType) { | ||
return (content as unknown as ParagraphGroup).paragraphGroupNumber; | ||
} else if (Content.PARAGRAPH === content.contentType) { | ||
return (content as unknown as Paragraph).paragraphNumber; | ||
} else { | ||
return null; | ||
} | ||
} | ||
|
||
/** | ||
* @returns the `paragraphNumber` value of the first `Paragraph` that's a child of the given content, or `null` if no such `Paragraph` exists | ||
*/ | ||
function getFirstParagraphNumber<T extends ContentBase | ContentBase & ContentContainer>(content: T): number | null { | ||
const mainContentExists = hasMainContent(content); | ||
if (mainContentExists) { | ||
const paragraphs = getParagraphs([content as ContentBase & ContentContainer]); | ||
return paragraphs[0]?.paragraphNumber ?? null; | ||
} else { | ||
return null; | ||
} | ||
} | ||
//#endregion |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
Excepting this file, all files in this directory are programmatically generated, and should not be manually modified. |
Oops, something went wrong.