├── .github └── workflows │ └── deploy.yaml ├── .gitignore ├── LICENSE ├── README.md ├── app-wiki ├── tiddlers │ └── app │ │ ├── HelloThere.tid │ │ ├── Topics Map Guidelines.tid │ │ ├── _tw_shared │ │ └── tiddlywiki.files │ │ ├── config │ │ └── SearchResultsDefault.tid │ │ ├── images │ │ ├── favicon.png │ │ ├── favicon.png.meta │ │ ├── jack-in-links.svg │ │ └── jack-in-links.svg.meta │ │ ├── system │ │ ├── DefaultSearchResultListModified.tid │ │ ├── DefaultTiddlers.tid │ │ ├── ListItemTemplateModified.tid │ │ ├── SiteSubtitle.tid │ │ ├── SiteTitle.tid │ │ ├── Startup Settings.tid │ │ ├── TopicsMap.txt │ │ ├── TopicsMap.txt.meta │ │ ├── links-palette.tid │ │ └── palette.tid │ │ ├── templates │ │ ├── Components │ │ │ ├── LinkPanel.tid │ │ │ ├── LinkPanelSearch.tid │ │ │ ├── LinksTagTemplate.tid │ │ │ └── MacroListTaggedDraggableForOriginalTitle.tid │ │ ├── Details │ │ │ ├── Contributor.tid │ │ │ ├── Link.tid │ │ │ └── Topic.tid │ │ ├── RichLink │ │ │ ├── Contributor.tid │ │ │ ├── Link.tid │ │ │ ├── RichTitleLink.tid │ │ │ └── Topic.tid │ │ ├── Styles.tid │ │ └── ViewTemplateBodyCascade.tid │ │ └── top-level-pages │ │ ├── About.tid │ │ ├── Contributing.tid │ │ ├── Contributors.tid │ │ ├── Documentation.tid │ │ ├── Latest.tid │ │ ├── Links.tid │ │ ├── Search.tid │ │ └── Topics.tid └── tiddlywiki.info ├── bin ├── clean.sh ├── collect-links.js ├── test.js ├── tiddlywiki-utils.js └── utils.js ├── package-lock.json ├── package.json └── sites └── sites.json /.github/workflows/deploy.yaml: -------------------------------------------------------------------------------- 1 | name: Build and Deploy TiddlyWiki Links 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | tags: 8 | - "*" 9 | schedule: 10 | - cron: '*/17 * * * *' 11 | 12 | # Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages 13 | permissions: 14 | contents: write 15 | pages: write 16 | id-token: write 17 | 18 | jobs: 19 | build-and-deploy: 20 | runs-on: ubuntu-latest 21 | steps: 22 | - name: Checkout Source 23 | uses: actions/checkout@v4.1.1 24 | with: 25 | persist-credentials: false 26 | - name: Install dependencies 27 | run: npm install 28 | - name: Run Tests 29 | run: npm test 30 | - name: Build Site 31 | run: npm run build 32 | - name: Upload artifact 33 | uses: actions/upload-pages-artifact@v3.0.1 34 | with: 35 | path: ./dist 36 | - name: Deploy to GitHub Pages 37 | id: deployment 38 | uses: actions/deploy-pages@v4.0.4 39 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules/ 3 | dist/ 4 | output/ 5 | app-wiki/tiddlers/sites/ 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 jeremy@jermolene.com 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. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # TiddlyWiki Links 2 | 3 | 4 | ## Setup 5 | 6 | Clone this repository and install its dependencies: 7 | 8 | ``` 9 | npm install 10 | ``` 11 | 12 | To install subsequent upstream changes from TiddlyWiki 5: 13 | 14 | ``` 15 | npm update 16 | ``` 17 | 18 | ## Develop 19 | 20 | To build the app locally and serve it over HTTP: 21 | 22 | ``` 23 | npm start 24 | ``` 25 | 26 | To edit tiddlers over HttP without downloading link tiddlers. You must have previously downloaded the link tiddlers via `npm start` or other command. A random port will be used if port 8080 is unavailable. 27 | 28 | ``` 29 | npm run edit 30 | ``` 31 | 32 | ## Build 33 | 34 | To build the app locally: 35 | 36 | ``` 37 | npm run build 38 | ``` 39 | 40 | The output files will be in `/output`. 41 | 42 | It is recommended to view the output files via the HTTTP server. The links in the output files won't work correctly if they are viewed via the `file://` protocol (in particular, unlike a webserver, the `file://` protocol does not automatically add `/index.html` to URLs that reference a directory). 43 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/HelloThere.tid: -------------------------------------------------------------------------------- 1 | created: 20220420182318455 2 | modified: 20220514192858255 3 | title: HelloThere 4 | type: text/vnd.tiddlywiki 5 | 6 | !! Welcome to the ~TiddlyWiki Links Aggregator 7 | 8 | Learn more about this project and how you can participate by clicking on the `About` tab below 9 | 10 | <> 11 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/Topics Map Guidelines.tid: -------------------------------------------------------------------------------- 1 | created: 20220517231532213 2 | modified: 20220517233810657 3 | tags: 4 | title: Topics Map Guidelines 5 | type: text/vnd.tiddlywiki 6 | 7 | The [[Topics Map]] is used during the links import process to convert the topics supplied by a dozen contributors into a somewhat more consistent set of Topics. 8 | 9 | These are some general guidelines to consider when entering items into the [[Topics Map]]. 10 | 11 | * Prefer British terminology 12 | * Avoid camel case except for terms that always use camel case (e.g. "TiddlyWiki", "CamelCase") 13 | * Avoid acronyms except when well known or common 14 | * Prefer title case for output (e.g. "War and Peace") 15 | * Prefer singular over plural for items that are a member of a group 16 | * Consider using -ing when the singular form doesn't make sense. 17 | 18 | These are just suggested guidelines for helping to create a unified and consistent set of Topics. Naturally, there will be times when it is useful to not follow the exact guidelines. 19 | 20 | !!! Structure of the [[Topics Map]] . 21 | 22 | * Comma delimited 23 | * First column is original term converted to strict title case (e.g. "War And Peace") 24 | * Second column is translated term 25 | * Put quotes around any original or translated terms that contain commas 26 | * The hashtag symbol as the first character on a line denotes a comment 27 | * Please retain the comments on the first line as a reminder to anyone who doesn't see these guidelines. 28 | 29 | !!! Usage 30 | 31 | Any proposed changes to the [[Topics Map]] should made as a PR to https://github.com/TiddlyWiki/TiddlyWikiLinks -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/_tw_shared/tiddlywiki.files: -------------------------------------------------------------------------------- 1 | { 2 | "directories": [ 3 | "../../../../node_modules/tiddlywiki/editions/tw5.com/tiddlers/_tw_shared" 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/config/SearchResultsDefault.tid: -------------------------------------------------------------------------------- 1 | created: 20220528213506583 2 | modified: 20220528213510009 3 | title: $:/config/SearchResults/Default 4 | type: text/vnd.tiddlywiki 5 | 6 | $:/core/ui/DefaultSearchResultListModified -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TiddlyWiki/TiddlyWikiLinks/ffcc6f3c5b704cc897ca6093e9864fbe2a52a053/app-wiki/tiddlers/app/images/favicon.png -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/images/favicon.png.meta: -------------------------------------------------------------------------------- 1 | title: $:/favicon.ico 2 | type: image/png 3 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/images/jack-in-links.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/images/jack-in-links.svg.meta: -------------------------------------------------------------------------------- 1 | title: $:/_Images/JackInLinks.svg 2 | type: image/svg+xml 3 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/system/DefaultSearchResultListModified.tid: -------------------------------------------------------------------------------- 1 | caption: Orig. Title 2 | created: 20220528211517501 3 | first-search-filter: [!is[system]search:original-titlesort[original-title]limit[250]] 4 | modified: 20220528213902097 5 | second-search-filter: [!is[system]search:original-title,textsort[original-title]limit[250]] 6 | tags: $:/tags/SearchResults 7 | title: $:/core/ui/DefaultSearchResultListModified 8 | type: text/vnd.tiddlywiki 9 | 10 | \define searchResultList() 11 | \whitespace trim 12 | //{{$:/language/Search/Matches/Title}}// 13 | 14 | <$list filter="[minlength[1]]" variable="ignore"> 15 | <$list filter={{{ [get[first-search-filter]] }}}> 16 | addsuffix[-primaryList]] -[get[text]] +[then[]else[tc-list-item-selected]] }}}> 17 | <$transclude tiddler="$:/core/ui/ListItemTemplateModified"/> 18 | 19 | 20 | 21 | 22 | //{{$:/language/Search/Matches/All}}// 23 | 24 | <$list filter="[minlength[1]]" variable="ignore"> 25 | <$list filter={{{ [get[second-search-filter]] }}}> 26 | addsuffix[-secondaryList]] -[get[text]] +[then[]else[tc-list-item-selected]] }}}> 27 | <$transclude tiddler="$:/core/ui/ListItemTemplateModified"/> 28 | 29 | 30 | 31 | 32 | \end 33 | <> 34 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/system/DefaultTiddlers.tid: -------------------------------------------------------------------------------- 1 | title: $:/DefaultTiddlers 2 | 3 | HelloThere 4 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/system/ListItemTemplateModified.tid: -------------------------------------------------------------------------------- 1 | created: 20220528213416105 2 | modified: 20220528213608928 3 | tags: 4 | title: $:/core/ui/ListItemTemplateModified 5 | type: text/vnd.tiddlywiki 6 | 7 |
<$link><$view field="original-title"/>
-------------------------------------------------------------------------------- /app-wiki/tiddlers/app/system/SiteSubtitle.tid: -------------------------------------------------------------------------------- 1 | title: $:/SiteSubtitle 2 | text: ~TiddlyWiki community links -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/system/SiteTitle.tid: -------------------------------------------------------------------------------- 1 | title: $:/SiteTitle 2 | text: [img width=200 [$:/_Images/JackInLinks.svg]]
links.tiddlywiki.org -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/system/Startup Settings.tid: -------------------------------------------------------------------------------- 1 | created: 20220513192040502 2 | modified: 20220521183547930 3 | tags: $:/tags/StartupAction 4 | title: Startup Settings 5 | type: text/vnd.tiddlywiki 6 | 7 | <$action-setfield $tiddler="$:/state/sidebar" text=“no” /> 8 | <$action-setfield $tiddler="$:/temp/search" text="cloud"/> -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/system/TopicsMap.txt: -------------------------------------------------------------------------------- 1 | # "old topic", "mapped topic" 2 | Actionwidget,Widget 3 | Aliases,Alias 4 | Aws, Amazon 5 | Abc Notation,ABC Notation 6 | Advanced-search,Advanced Search 7 | Advanced-tools,Advanced Tools 8 | Blog, Blog Post 9 | Blogpost, Blogging 10 | Camelcase,CamelCase 11 | Cmdb,CMDB 12 | Code,Coding 13 | Colorpalettes, ColorPalette 14 | Colorpalette, ColorPalette 15 | Color, Colour 16 | Controlpanel, Control Panel 17 | Cross-reference,Cross Reference 18 | Css, CSS 19 | Csv, CSV 20 | D&d, D&D 21 | Dataexchange,Data Exchange 22 | Date,Dates and Time 23 | Dates-and-time,Dates and Time 24 | Deprecated, Obsolete 25 | Dev,Developer 26 | Development,Developer 27 | Diagrams, Diagraming 28 | Disused, Obsolete 29 | Dropdowns, Dropdown 30 | Editing Tool,Editing 31 | Export, Exporting 32 | Editions, Edition 33 | Editor Stamps, Editor 34 | Editors, Editor 35 | Example Wiki, Example 36 | Examples, Example 37 | External, Files 38 | External Files, Files 39 | Filters, Filter 40 | Filteroperators, "Filter Operators" 41 | Gtd,GTD 42 | Handyfilters, "Filter" 43 | Hastags, Hashtags 44 | Howto, How To 45 | Import, Importing 46 | Interface, Interfacing 47 | Ides, IDEs 48 | Info-section,Info Section 49 | Isbn, ISBN 50 | Json, JSON 51 | Journal, Journaling 52 | Journals, Journaling 53 | Lists, Listing 54 | Markdown, Markdown 55 | Md, Markdown 56 | Mobile, Mobile Tools 57 | Multi Column, Multi-Columns 58 | Making-plugins, "Plugin Making" 59 | Maps, Mapping 60 | Math, Maths 61 | memorization, Memory 62 | Non-modern, Obsolete 63 | Nodejs, Node.js 64 | Outline, Outliners 65 | Pdf,PDF 66 | Plugins, Plugin 67 | Popups, Popup 68 | Projectmanagement, Project Management 69 | Proof Of Concept, Proof of Concept 70 | Randomization, Randomisation 71 | Regexp, Regular Expression 72 | Regular Expressions, "Regular Expression" 73 | Roamresearch, Roam Research 74 | Roam-like, Roam Research 75 | Rougelike, Rouge 76 | Rss,RSS 77 | Savers,Saving 78 | Screenplay-creation,Screenplay Creation 79 | Script,Scripting 80 | Search, Searching 81 | Search-replace, Searching 82 | Searching Tools, Searching 83 | Social-media,"Social Media" 84 | Spreadsheet, Spreadsheets 85 | Static Site,Static Sites 86 | Static-pages,Static Sites 87 | Staticsite,Static Sites 88 | Syntaxhighlighting,Syntax Highlighting 89 | Svg,SVG 90 | Tabs,Tabbing 91 | Table-of-contents,Table of Contents 92 | Tableofcontents,Table of Contents 93 | Taghierarchy,"Tagging" 94 | Tags,Tagging 95 | Tags-linkstoryriver,Tagging 96 | Task-management,Task Management 97 | Text Editor,Editor 98 | Text-manipulation,"Text Manipulation" 99 | Themes, Theme 100 | Tiddler-creation,"Tiddler Creation" 101 | Tiddler-management,"Tiddler Management" 102 | Tiddler-manipulation,"Tiddler Manipulation" 103 | Tiddlytalk, TiddlyTalk 104 | Tiddlwikiclassic,"TiddlWiki Classic" 105 | Tiddlywiki Help,TiddlyWiki Help 106 | Tiddlywiki-online,TiddlyWiki Online 107 | Tiddlywikitoolmap,TiddlyWiki Toolmap 108 | Timer, Time and Date 109 | To-do,To Do 110 | Toc,"Table of Contents" 111 | Todo,To Do 112 | Topmenu,Top Menu 113 | Tutorials, Tutorial 114 | Twclassic,TiddlWiki Classic 115 | Ui,UI 116 | Understanding-tiddlywiki,Understanding TiddlyWiki 117 | Usecases,Use Cases 118 | Usedbyst,Used by ST 119 | Userscript, Scripts 120 | Web-hosting,Web Hosting 121 | Widgets,Widget 122 | Wikitex-extensions,WikiText 123 | Wikitext,WikiText 124 | Wip, WIP 125 | Wordcounts,Word Counts 126 | Workflows,Work Flows 127 | Writing-paragraphs,Writing 128 | Wysiwyg,WYSIWYG 129 | Xml,XML -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/system/TopicsMap.txt.meta: -------------------------------------------------------------------------------- 1 | created: 20220516171708086 2 | modified: 20220517202005674 3 | tags: 4 | title: Topics Map 5 | type: text/plain -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/system/links-palette.tid: -------------------------------------------------------------------------------- 1 | title: $:/_Palette 2 | name: TiddlyWikiLinks 3 | description: TiddlyWikiLinks Custom Palette 4 | tags: $:/tags/Palette 5 | type: application/x-tiddler-dictionary 6 | 7 | alert-background: #d5eee8 8 | alert-border: #75586e 9 | alert-highlight: #82d336 10 | alert-muted-foreground: #82d336 11 | background: #d5eee8 12 | blockquote-bar: #82d336 13 | button-background: 14 | button-border: 15 | button-foreground: 16 | code-background: #d5eee8 17 | code-border: #a193a1 18 | code-foreground: #75586e 19 | dirty-indicator: inherit 20 | download-background: #008599 21 | download-foreground: #d5eee8 22 | dragger-background: #d5eee8 23 | dragger-foreground: #83657b 24 | dropdown-background: #d5eee8 25 | dropdown-border: #75586e 26 | dropdown-tab-background: #e3fdf6 27 | dropdown-tab-background-selected: #d5eee8 28 | dropzone-background: #008599 29 | external-link-background: inherit 30 | external-link-background-hover: inherit 31 | external-link-background-visited: inherit 32 | external-link-foreground: #d2268b 33 | external-link-foreground-hover: inherit 34 | external-link-foreground-visited: #d2268b 35 | foreground: #83657b 36 | message-background: #e3fdf6 37 | message-border: #a193a1 38 | message-foreground: #83657b 39 | modal-backdrop: #968394 40 | modal-background: #e3fdf6 41 | modal-border: #a193a1 42 | modal-footer-background: #d5eee8 43 | modal-footer-border: #a193a1 44 | modal-header-border: #a193a1 45 | muted-foreground: #75586e 46 | notification-background: #e3fdf6 47 | notification-border: #a193a1 48 | page-background: #d5eee8 49 | pre-background: #d5eee8 50 | pre-border: #968394 51 | primary: #008599 52 | select-tag-background: #e3fdf6 53 | select-tag-foreground: #83657b 54 | sidebar-button-foreground: #75586e 55 | sidebar-controls-foreground: #75586e 56 | sidebar-controls-foreground-hover: #82d336 57 | sidebar-foreground: #75586e 58 | sidebar-foreground-shadow: transparent 59 | sidebar-muted-foreground: #83657b 60 | sidebar-muted-foreground-hover: #75586e 61 | sidebar-tab-background: #e3fdf6 62 | sidebar-tab-background-selected: #d5eee8 63 | sidebar-tab-border: #d5eee8 64 | sidebar-tab-border-selected: #83657b 65 | sidebar-tab-divider: #e3fdf6 66 | sidebar-tab-foreground: #968394 67 | sidebar-tab-foreground-selected: #75586e 68 | sidebar-tiddler-link-foreground: #982aa1 69 | sidebar-tiddler-link-foreground-hover: #36002b 70 | site-title-foreground: #82d336 71 | static-alert-foreground: #75586e 72 | tab-background: #d5eee8 73 | tab-background-selected: #e3fdf6 74 | tab-border: #a193a1 75 | tab-border-selected: #75586e 76 | tab-divider: #75586e 77 | tab-foreground: #83657b 78 | tab-foreground-selected: #75586e 79 | table-border: #a193a1 80 | table-footer-background: #d5eee8 81 | table-header-background: #d5eee8 82 | tag-background: #00b589 83 | tag-foreground: #e3fdf6 84 | tiddler-background: #e3fdf6 85 | tiddler-border: #a193a1 86 | tiddler-controls-foreground: inherit 87 | tiddler-controls-foreground-hover: #82d336 88 | tiddler-controls-foreground-selected: #982aa1 89 | tiddler-editor-background: #e3fdf6 90 | tiddler-editor-border: #d5eee8 91 | tiddler-editor-border-image: #e3fdf6 92 | tiddler-editor-fields-even: #e3fdf6 93 | tiddler-editor-fields-odd: #d5eee8 94 | tiddler-info-background: #d5eee8 95 | tiddler-info-border: #968394 96 | tiddler-info-tab-background: #e3fdf6 97 | tiddler-link-background: #e3fdf6 98 | tiddler-link-foreground: #982aa1 99 | tiddler-subtitle-foreground: #83657b 100 | tiddler-title-foreground: #82d336 101 | toolbar-cancel-button: #83657b 102 | toolbar-close-button: #83657b 103 | toolbar-delete-button: #2fdc32 104 | toolbar-done-button: #83657b 105 | toolbar-edit-button: #83657b 106 | toolbar-info-button: #83657b 107 | toolbar-new-button: #83657b 108 | toolbar-options-button: #83657b 109 | toolbar-save-button: inherit 110 | untagged-background: #75586e 111 | very-muted-foreground: #a193a1 112 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/system/palette.tid: -------------------------------------------------------------------------------- 1 | title: $:/palette 2 | text: $:/palettes/CupertinoDark 3 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/templates/Components/LinkPanel.tid: -------------------------------------------------------------------------------- 1 | title: $:/_Templates/Components/LinkPanel 2 | 3 | 13 | 40 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/templates/Components/LinkPanelSearch.tid: -------------------------------------------------------------------------------- 1 | created: 20220420192043799 2 | modified: 20230812005113559 3 | tags: 4 | title: $:/_Templates/Components/LinkPanelSearch 5 | type: text/vnd.tiddlywiki 6 | 7 | 17 | <$let splithere="^(.{1,30})(.*)" linereturn=""" 18 | """ 19 | pseudo={{{ [get[text]splitregexpsearch-replace:im:regexp,[$1]] }}} 20 | workingTitle={{{ [{Search!!title-type}match[original]then{!!original-title}else] }}} 21 | tv-ellipsis={{{ [{Search!!title-type}match[original]then[]else[...]] }}} 22 | > 23 | 40 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/templates/Components/LinksTagTemplate.tid: -------------------------------------------------------------------------------- 1 | created: 20230813003423274 2 | modified: 20230813192650652 3 | tags: 4 | title: $:/Links-TagTemplate 5 | type: text/vnd.tiddlywiki 6 | 7 | \import [[$:/macro-list-tagged-draggable-for-original-title]] 8 | \whitespace trim 9 | >> 10 | <$set name="transclusion" value=<>> 11 | <$macrocall $name="tag-pill-body" tag=<> icon={{{ [] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerIconFilter]!is[draft]get[text]] }}} colour={{{ [] :cascade[all[shadows+tiddlers]tag[$:/tags/TiddlerColourFilter]!is[draft]get[text]] }}} palette={{$:/palette}} element-tag="""$button""" element-attributes="""popup=<> dragFilter='[all[current]tagging[]]' tag='span'"""/> 12 | <$reveal state=<> type="popup" position="below" animate="yes" class="tc-drop-down"> 13 | <$set name="tv-show-missing-links" value="yes"> 14 | <$transclude tiddler="$:/core/ui/ListItemTemplate"/> 15 | 16 | <$list filter="[all[shadows+tiddlers]tag[$:/tags/TagDropdown]!has[draft.of]]" variable="listItem"> 17 | <$transclude tiddler=<>/> 18 | 19 |
20 | <$macrocall $name="list-tagged-draggable" tag=<> subFilter="sort[original-title]"/> 21 | 22 | 23 |
24 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/templates/Components/MacroListTaggedDraggableForOriginalTitle.tid: -------------------------------------------------------------------------------- 1 | created: 20230813003249302 2 | modified: 20230813192530457 3 | tags: 4 | title: $:/macro-list-tagged-draggable-for-original-title 5 | type: text/vnd.tiddlywiki 6 | 7 | \define list-tagged-draggable(tag,subFilter,emptyMessage,itemTemplate,elementTag:"div",storyview:"") 8 | \whitespace trim 9 | 10 | <$set name="tag" value=<<__tag__>>> 11 | <$list filter="[<__tag__>tagging[]$subFilter$]" emptyMessage=<<__emptyMessage__>> storyview=<<__storyview__>>> 12 | <$elementTag$ class="tc-menu-list-item"> 13 | <$droppable actions="""<$macrocall $name="list-tagged-draggable-drop-actions" tag=<<__tag__>>/>""" enable=<>> 14 | <$elementTag$ class="tc-droppable-placeholder"/> 15 | <$elementTag$> 16 | <$transclude tiddler="""$itemTemplate$"""> 17 | <$link to={{!!title}}> 18 | <$view field="original-title"/> 19 | 20 | 21 | 22 | 23 | 24 | 25 | <$tiddler tiddler=""> 26 | <$droppable actions="""<$macrocall $name="list-tagged-draggable-drop-actions" tag=<<__tag__>>/>""" enable=<>> 27 | <$elementTag$ class="tc-droppable-placeholder"/> 28 | <$elementTag$ style="height:0.5em;"> 29 | 30 | 31 | 32 | 33 | 34 | \end -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/templates/Details/Contributor.tid: -------------------------------------------------------------------------------- 1 | title: $:/_Templates/Details/Contributor 2 | 3 |

{{||$:/_Templates/RichLink/Contributor}}

4 |

5 | Publishing <$count filter="[tag[$:/tags/Link]field:origin{!!name}]"/> links from: <$link href={{!!url}} target="_blank" rel="noopener noreferrer"><$text text={{!!url}}/> 6 |

7 |

8 | <$transclude mode="inline"/> 9 |

10 |
    11 | <$let tv-linkpanel-show-url="yes"> 12 | <$list filter="[tag[$:/tags/Link]field:origin{!!name}!sort[modified]]"> 13 |
  • 14 | {{||$:/_Templates/Components/LinkPanel}} 15 |
  • 16 | 17 | 18 |
19 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/templates/Details/Link.tid: -------------------------------------------------------------------------------- 1 | title: $:/_Templates/Details/Link 2 | 3 | <$let tv-linkpanel-show-contributor="yes" tv-linkpanel-show-url="yes"> 4 | {{||$:/_Templates/Components/LinkPanel}} 5 | 6 | 7 | Bookmarked by <$count filter="[tag[$:/tags/Link]field:url{!!url}each[origin]] -[]"/> other contributor(s) 8 | 9 |
    10 | <$list filter="[tag[$:/tags/Link]field:url{!!url}!sort[created]] -[]"> 11 |
  • 12 | <$let tv-linkpanel-show-contributor="yes" tv-linkpanel-show-url="no"> 13 | {{||$:/_Templates/Components/LinkPanel}} 14 | 15 |
  • 16 | 17 |
18 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/templates/Details/Topic.tid: -------------------------------------------------------------------------------- 1 | title: $:/_Templates/Details/Topic 2 | 3 | <$let 4 | topicTitle={{{ [tag[$:/tags/Topic]] :else[all[shadows+tiddlers]tag[$:/tags/Topic]field:topic] }}} 5 | topicName={{{ [get[topic]] }}} 6 | > 7 | 8 |

<$vars count={{{ [tag[$:/tags/Link]tagcount[]] }}}>{{||$:/_Templates/RichLink/Topic}}

9 | 10 | Links in this topic: 11 | 12 | <$let topic={{!!topic}}> 13 |
    14 | <$list filter="[tag[$:/tags/Link]tageach[url]sort[url]]"> 15 |
  • 16 |

    {{||$:/_Templates/RichLink/Link}}

    17 |
      18 | <$list filter="[tag[$:/tags/Link]tagfield:url{!!url}!sort[modified]]"> 19 |
    • 20 | <$vars tv-linkpanel-show-contributor="yes" tv-linkpanel-show-url="no"> 21 | {{||$:/_Templates/Components/LinkPanel}} 22 | 23 |
    • 24 | 25 |
    26 |
  • 27 | 28 |
29 | 30 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/templates/RichLink/Contributor.tid: -------------------------------------------------------------------------------- 1 | title: $:/_Templates/RichLink/Contributor 2 | 3 | 9 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/templates/RichLink/Link.tid: -------------------------------------------------------------------------------- 1 | title: $:/_Templates/RichLink/Link 2 | 3 | 8 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/templates/RichLink/RichTitleLink.tid: -------------------------------------------------------------------------------- 1 | created: 20220421151132670 2 | modified: 20230813192731115 3 | title: $:/_Templates/RichTitle/Link 4 | type: text/vnd.tiddlywiki 5 | 6 | 27 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/templates/RichLink/Topic.tid: -------------------------------------------------------------------------------- 1 | title: $:/_Templates/RichLink/Topic 2 | 3 | \whitespace trim 4 | <$let 5 | topicTitle={{{ [tag[$:/tags/Topic]] :else[all[shadows+tiddlers]tag[$:/tags/Topic]field:topic] }}} 6 | topicName={{{ [get[topic]] }}} 7 | > 8 | 20 | 21 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/templates/Styles.tid: -------------------------------------------------------------------------------- 1 | created: 20220423154207929 2 | modified: 20230812003929766 3 | tags: $:/tags/Stylesheet 4 | title: $:/_Styles 5 | type: text/vnd.tiddlywiki 6 | 7 | \import [[$:/core/ui/PageMacros]] [all[shadows+tiddlers]tag[$:/tags/Macro]!has[draft.of]] 8 | \rules only filteredtranscludeinline transcludeinline macrodef macrocallinline macrocallblock 9 | \define dynannotate-dark-styles() 10 | <$list filter="[{$:/palette}match[$:/palettes/CupertinoDark]]"> 11 | .tc-link-panel > .tc-link-panel-body { 12 | background: #3f638b ; 13 | padding: 0.5em; 14 | } 15 | 16 | .tc-dynannotate-snippet-context::selection { 17 | background-color: lightgray ; 18 | color: black; 19 | } 20 | .tc-dynannotate-snippet-highlight { 21 | color: black ; 22 | } 23 | 24 | a.tc-richlink-link-target:visited svg { 25 | fill: lightgray; 26 | 27 | } 28 | 29 | \end 30 | 31 | /* 32 | Rich links 33 | */ 34 | 35 | .tc-richlink { 36 | } 37 | 38 | .tc-richlink a { 39 | text-decoration: none; 40 | } 41 | 42 | .tc-richlink-contributor { 43 | font-size: 1.2em; 44 | font-weight: bold; 45 | } 46 | 47 | .tc-richlink-contributor img { 48 | width: 1em; 49 | height: 1em; 50 | } 51 | 52 | tc-richlink-contributor-name { 53 | } 54 | 55 | .tc-richlink-link { 56 | 57 | } 58 | 59 | .tc-richlink div.tc-tags-wrapper { 60 | margin: 0 0 0 0 ; 61 | } 62 | 63 | 64 | .tc-richlink-link-info { 65 | word-break: break-all; 66 | } 67 | 68 | .tc-richlink-link-target { 69 | text-decoration: none; 70 | } 71 | 72 | .tc-richlink-link-target:hover { 73 | background-color: <>; 74 | } 75 | 76 | .tc-richlink-link-target { 77 | vertical-align: middle; 78 | } 79 | 80 | .tc-richlink a svg { 81 | width: 1em; 82 | height: 1em; 83 | } 84 | 85 | .tc-richlink-topic { 86 | display: inline-block; 87 | font-size: 0.8em; 88 | line-height: 1.2; 89 | } 90 | 91 | .tc-richlink-topic a { 92 | display: inline-block; 93 | padding: 0.25em 0.5em; 94 | margin: 0.5em; 95 | border-radius: 1em; 96 | text-decoration: none; 97 | background-color: <>; 98 | color: <>; 99 | font-weight: bold; 100 | } 101 | 102 | .tc-richlink-topic a .tc-richlink-topic-name { 103 | vertical-align: bottom; 104 | } 105 | 106 | 107 | .tc-richlink-topic a .tc-richlink-topic-count { 108 | display: inline-block; 109 | font-size: 0.8em; 110 | padding: 0 0.25em; 111 | border-radius: 1em; 112 | background: <>; 113 | color: <>; 114 | margin: 0 0.25em 0 0.5em; 115 | vertical-align: text-bottom; 116 | } 117 | 118 | /* 119 | Details 120 | */ 121 | 122 | .tc-contributor-info { 123 | border: 1px solid <>; 124 | border-radius: 0.25em; 125 | background-color: #fff8f8; 126 | padding: 1em; 127 | } 128 | 129 | .tc-list-horizontal { 130 | list-style: none; 131 | padding: 0; 132 | } 133 | 134 | .tc-list-horizontal > li { 135 | display: inline; 136 | } 137 | 138 | .tc-list-vertical { 139 | list-style: none; 140 | padding: 0; 141 | } 142 | 143 | .tc-list-vertical > li { 144 | margin: 1em 0; 145 | } 146 | 147 | .tc-link-panel { 148 | border: 1px solid <>; 149 | border-radius: 0.25em; 150 | margin: 1em 0 1em 0; 151 | } 152 | 153 | .tc-link-panel > .tc-link-panel-heading { 154 | background-color: <>; 155 | line-height: 1.7; 156 | padding-left: 0.5em; 157 | } 158 | 159 | .tc-link-panel > .tc-link-panel-heading > .tc-link-panel-subheading { 160 | font-size: 0.8em; 161 | } 162 | 163 | .tc-link-panel > .tc-link-panel-topics { 164 | background: #fff8f8; 165 | } 166 | 167 | .tc-link-panel > .tc-link-panel-topics .tc-list-horizontal { 168 | margin: 0; 169 | } 170 | 171 | .tc-link-panel > .tc-link-panel-body { 172 | background: #fff; 173 | padding: 0.5em; 174 | } 175 | 176 | <> 177 | 178 | /* 179 | Search 180 | */ 181 | 182 | .tc-section-search { 183 | position: relative; 184 | text-align: right; 185 | padding: 0.25em 0.25em 0.25em 0; 186 | } 187 | 188 | #tid-page-search-output { 189 | text-align: left; 190 | max-width: calc(100% - 2em); 191 | margin: 0 1em 0 1em; 192 | position: absolute; 193 | border: 1px solid black; 194 | background: white; 195 | padding: 0 1em 1em 1em; 196 | border-radius: 0.5em; 197 | box-shadow: 5px 5px 5px rgba(0, 0, 0, 0.3); 198 | } 199 | 200 | div.tc-link-search { 201 | display: flex; 202 | align-items: flex-start; 203 | } 204 | div.tc-link-search input { 205 | flex: 1; /*take all the available space*/ 206 | max-width: 25em ; 207 | } 208 | div.tc-link-search span{ 209 | padding-left: 7px; 210 | padding-right: 7px; 211 | } 212 | 213 | table.tc-search-options td, table.tc-search-options { 214 | border: none; 215 | } 216 | 217 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/templates/ViewTemplateBodyCascade.tid: -------------------------------------------------------------------------------- 1 | title: $:/_ViewTemplateBodyCascade 2 | tags: $:/tags/ViewTemplateBodyFilter 3 | code-body: yes 4 | list-before: 5 | 6 | [tag[$:/tags/Link]then[$:/_Templates/Details/Link]] 7 | :else[tag[$:/tags/Topic]then[$:/_Templates/Details/Topic]] 8 | :else[tag[$:/tags/Contributor]then[$:/_Templates/Details/Contributor]] 9 | :else[tag[$:/tags/AvataXXXXXXr]then[$:/_Templates/Details/Avatar]] -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/top-level-pages/About.tid: -------------------------------------------------------------------------------- 1 | created: 20220513191216286 2 | modified: 20220517233828350 3 | tags: 4 | title: About 5 | type: text/vnd.tiddlywiki 6 | 7 | This site is a collection of links to useful and interesting ~TiddlyWiki material curated by our team of community editors. 8 | 9 | !! How This Site Works 10 | 11 | This site aggregates links curated by individual members of the ~TiddlyWiki community. It lets you see the latest links, and explore them through categories and timelines. 12 | 13 | This site works best with a crowd of people posting links. The pressure on individuals is reduced because not everybody needs to catch every interesting link that flies past. The aggregation effects reduce the impact of mistakes. For example, if one person mis-tags a link under an inappropriate topic, the site will show that only one person added that tag, versus the majority using more appropriate tags. In that way, we hope that a sort of wisdom of the crowds will emerge, with consensus emerging as to the most useful ways to describe and categorise links. 14 | 15 | * [[How to contribute links to this site|Contributing]] 16 | * [[Find out how this site works|Documentation]] 17 | 18 | Some topics are combined and relabeled during the link import process. Read more here: 19 | 20 | * [[Topics Map Guidelines]] 21 | 22 | Last update: <$text text={{{ [tag[$:/tags/Link]!sort[modified]get[modified]limit[1]format:date{$:/language/Tiddler/DateFormat}] }}}/> 23 | 24 | Also available as raw tiddlers in a JSON file. 25 | 26 | Join the development of [[TiddlyWikiLinks on GitHub|https://github.com/TiddlyWiki/TiddlyWikiLinks]] -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/top-level-pages/Contributing.tid: -------------------------------------------------------------------------------- 1 | title: Contributing 2 | 3 | !! How To Contribute 4 | 5 | We invite you to become a contributing editor for ~TiddlyWikiLinks. 6 | 7 | The technical requirement is that you must maintain a publicly accessible ~TiddlyWiki containing the links. The URL of your site must be registered in the [[sites.json|https://github.com/TiddlyWiki/TiddlyWikiLinks/blob/main/sites/sites.json]] file in the ~GitHub repository. You can go ahead and add yourself if you know how to send a ~GitHub pull request, or you can post a message to the forum asking for somebody else to create the PR on your behalf. The information needed in [[sites.json|https://github.com/TiddlyWiki/TiddlyWikiLinks/blob/main/sites/sites.json]] is: 8 | 9 | * Your username, which must be unique 10 | * The URL of your site 11 | 12 | The links must be stored as individual tiddlers tagged `$:/tags/Link` with the following fields: 13 | 14 | * ''created'' -- the timestamp the link was created 15 | * ''modified'' -- the timestamp the link was updated 16 | * ''tags'' -- the tag `$:/tags/Link` plus any other tags to describe the link (other system tags starting `$:/` are ignored) 17 | * ''text'' -- a brief comment describing the link that ''must only use inline formatting'' 18 | * ''title'' -- ignored by the system 19 | * ''url'' -- the URL of the link 20 | 21 | Your wiki should also have the following system tiddlers: 22 | 23 | * ''~$:/favicon.ico'' -- small square image used as your avator 24 | * ''~$:/SiteTitle'' -- a brief description of your links collection (must be plain text) 25 | * ''~$:/SiteSubtitle'' -- a longer description of your links collection (must be plain text) 26 | 27 | !! Guidelines 28 | 29 | * Your bookmarking wiki can be hosted anywhere on the web 30 | * The criteria for inclusion has some flexibility, but at a minimum the links should be of general interest to the ~TiddlyWiki community 31 | * This is not a personal bookmarking site. Contributing editors should think of themselves as part of a chorus of voices making up the material we publish on tiddlywiki.com 32 | * The system will ignore any other tiddlers in the wiki, but it is impolite to have too much extraneous material since the system must fetch the entire wiki at regular intervals 33 | * It is acceptable to post links to your own material, but please make it clear that that is the case 34 | 35 | !! Code of Conduct 36 | 37 | * Try to make people feel good 38 | * Do not use this platform to criticize or harass other people 39 | * Do not post links to offensive or illegal content 40 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/top-level-pages/Contributors.tid: -------------------------------------------------------------------------------- 1 | title: Contributors 2 | 3 | Number of contributors: <$count filter="[tag[$:/tags/Contributor]]"/> 4 | 5 |
    6 | <$list filter="[tag[$:/tags/Contributor]sort[]]"> 7 |
  • {{||$:/_Templates/RichLink/Contributor}} (<$count filter="[tag[$:/tags/Link]field:origin{!!name}]"/> links)
  • 8 | 9 |
10 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/top-level-pages/Documentation.tid: -------------------------------------------------------------------------------- 1 | title: Documentation 2 | 3 | !! Introduction 4 | 5 | The source code for the TiddlyWiki Links Aggregator is on GitHub: https://github.com/TiddlyWiki/TiddlyWikiLinks 6 | 7 | The Links Aggregator consists of three components: 8 | 9 | * ''./bin/collect-links.js'' - A standalone JavaScript application that downloads the contributor tiddlers from their respective wikis and transforms them into the data model described below 10 | * ''./app-wiki/'' - A TiddlyWiki application for interacting with the contributor tiddlers 11 | * ''./.github/workflows/deploy.yaml'' - GitHub Actions scripts to collect the contributor tiddlers, insert them into the TiddlyWiki application and publish the result via GitHub pages 12 | 13 | !! Data Model 14 | 15 | See [[Contributing]] for a discussion of the data model that contributing wikis must use. This section describes the data model used within this wiki. 16 | 17 | * <> - a description of a link provided by a contributor 18 | * <> - a contributor 19 | * <> - a topic 20 | * <> - the avator of a contibutor 21 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/top-level-pages/Latest.tid: -------------------------------------------------------------------------------- 1 | title: Latest 2 | 3 | The most recently updated links. 4 | 5 | <$vars subfilter="[tag[$:/tags/Link]]"> 6 | <$list filter="[subfilterhas[modified]!sort[modified]limit[10]eachday[modified]]"> 7 |

<$view field="modified" format="date" template="DDth MMM YYYY"/>

8 |
    9 | <$list filter="[sameday:modified{!!modified}subfilter!sort[modified]]"> 10 |
  • 11 | <$vars tv-linkpanel-show-contributor="yes" tv-linkpanel-show-url="yes"> 12 | {{||$:/_Templates/Components/LinkPanel}} 13 | 14 |
  • 15 | 16 |
17 | 18 | 19 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/top-level-pages/Links.tid: -------------------------------------------------------------------------------- 1 | title: Links 2 | 3 | Number of links: <$count filter="[tag[$:/tags/Link]]"/> 4 | 5 | Number of unique URLs: <$count filter="[tag[$:/tags/Link]each[url]]"/> 6 | 7 |
    8 | <$list filter="[tag[$:/tags/Link]each[url]sort[url]]"> 9 |
  • {{||$:/_Templates/RichLink/Link}} (<$count filter="[tag[$:/tags/Link]field:url{!!url}]"/> links)
  • 10 | 11 |
12 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/top-level-pages/Search.tid: -------------------------------------------------------------------------------- 1 | created: 20220420182241970 2 | modified: 20230813191116154 3 | search-mode: normal 4 | tags: 5 | title: Search 6 | title-type: original 7 | type: text/vnd.tiddlywiki 8 | 9 | 10 | 12 | 14 | 15 | 17 | 19 | 20 |
''Display as title:'' 11 | <$radio tiddler="Search" field="title-type" value="pseudo"> First characters. 13 | <$radio tiddler="Search" field="title-type" value="original"> Original title
''Search type:'' 16 | <$radio tiddler="Search" field="search-mode" value="normal"> Normal search 18 | <$radio tiddler="Search" field="search-mode" value="regexp"> Regular Exp.
21 | 22 | 25 | 26 | <$vars limit={{{ [{$:/temp/search}length[]compare:integer:lt[3]then[5]else[100]] }}}> 27 | <$let 28 | subfilter="[tag[$:/tags/Link]]" 29 | searchNormal="[search:text,original-title{$:/temp/search}]" 30 | searchRegexp="[regexp:text{$:/temp/search}][regexp:original-title{$:/temp/search}]" 31 | searchFilter= {{{ [{Search!!search-mode}match[normal]thenelse] }}} 32 | > 33 | <$list filter="[subfiltersubfilterlimit]"> 34 |
    35 |
  • 36 | <$vars tv-linkpanel-show-contributor="no" tv-linkpanel-show-url="yes"> 37 | {{||$:/_Templates/Components/LinkPanelSearch}} 38 | 39 |
  • 40 |
41 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /app-wiki/tiddlers/app/top-level-pages/Topics.tid: -------------------------------------------------------------------------------- 1 | title: Topics 2 | 3 | Number of topics: <$count filter="[tag[$:/tags/Link]tags[]] -[[$:/tags/Link]]"/> 4 | 5 |
    6 | <$list filter="[tag[$:/tags/Link]tags[]] -[[$:/tags/Link]] +[sort[]]"> 7 |
  • 8 | <$vars count={{{ [tag[$:/tags/Link]tagcount[]] }}}> 9 | {{||$:/_Templates/RichLink/Topic}} 10 | 11 |
  • 12 | 13 |
14 | -------------------------------------------------------------------------------- /app-wiki/tiddlywiki.info: -------------------------------------------------------------------------------- 1 | { 2 | "description": "TiddlyWiki Links", 3 | "plugins": [ 4 | "tiddlywiki/menubar", 5 | "tiddlywiki/highlight", 6 | "tiddlywiki/dynaview", 7 | "tiddlywiki/dynannotate" 8 | ], 9 | "themes": [ 10 | "tiddlywiki/vanilla", 11 | "tiddlywiki/snowwhite" 12 | ], 13 | "languages": [ 14 | ], 15 | "build": { 16 | "index": [ 17 | "--render","$:/core/save/all","index.html","text/plain", 18 | "--save","$:/favicon.ico","favicon.ico", 19 | "--render","$:/core/templates/exporters/JsonFile","tiddlers.json","text/plain","$:/core/templates/exporters/JsonFile", 20 | "exportFilter","[tag[$:/tags/Link]] [tag[$:/tags/Avatar]] [tag[$:/tags/Contributor]]"] 21 | }, 22 | "config": { 23 | "default-tiddler-location": "./tiddlers/app/", 24 | "retain-original-tiddler-path": true 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /bin/clean.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Remove output files 4 | 5 | rm -rf dist 6 | -------------------------------------------------------------------------------- /bin/collect-links.js: -------------------------------------------------------------------------------- 1 | /* 2 | Collect TiddlyWiki files containing 3 | */ 4 | 5 | const fs = require("fs"), 6 | path = require("path"), 7 | http = require("http"), 8 | https = require("https"), 9 | {promisify} = require("util"), 10 | readFileAsync = promisify(fs.readFile), 11 | writeFileAsync = promisify(fs.writeFile), 12 | mkdirAsync = promisify(fs.mkdir), 13 | fetch = require("node-fetch"), 14 | normalizeUrl = require('normalize-url'), 15 | {extractTiddlersFromWikiFile,parseStringArray,stringifyList} = require("./tiddlywiki-utils"), 16 | {ArgParser,hash} = require("./utils"), 17 | { parse } = require('csv-parse'); 18 | 19 | /* Create Topics mapping */ 20 | const topicsDict = {} 21 | 22 | fs.createReadStream('app-wiki/tiddlers/app/system/TopicsMap.txt') 23 | .pipe(parse({ 24 | comment: '#', 25 | trim: true, 26 | })) 27 | .on('data', (data) => { 28 | topicsDict[data[0]]=data[1] ; 29 | }) 30 | .on('error',(err) => { 31 | console.log(err) 32 | }) 33 | .on('end', ()=> { 34 | console.log("Done!") 35 | console.log(topicsDict) 36 | } ); 37 | 38 | class App { 39 | 40 | constructor(args) { 41 | // Parse arguments 42 | this.args = new ArgParser(args,{ 43 | defaults: { 44 | "sites": "./sites/sites.json", 45 | "output-tiddlers": "./app-wiki/tiddlers/sites/tiddlers.json" 46 | } 47 | }); 48 | } 49 | 50 | async main() { 51 | // Collect errors rather than failing on the first 52 | const errors = []; 53 | // Get arguments 54 | const sitesPath = this.args.byName["sites"][0], 55 | outputTiddlersPath = this.args.byName["output-tiddlers"][0]; 56 | // Retrieve and parse the bill of materials 57 | const sitesInfo = JSON.parse(await readFileAsync(sitesPath,"utf8")); 58 | // Collect up the output tiddlers and tags 59 | const output = [], outputTags = Object.create(null); 60 | // Go through each entry 61 | for(const siteInfo of sitesInfo) { 62 | console.log(siteInfo) 63 | // Read file 64 | const contents = await this.getFileContents(siteInfo.url); 65 | // Collect any errors 66 | if(contents.error) { 67 | errors.push({siteInfo: siteInfo, error: contents.error}); 68 | } else { 69 | // Collect a tiddler with information about the source 70 | var sourceDescriptionTiddler = { 71 | title: `Contributor: ${siteInfo.name}`, 72 | name: siteInfo.name, 73 | url: siteInfo.url, 74 | caption: "", 75 | text: "", 76 | tags: "$:/tags/Contributor" 77 | }; 78 | // Get the tiddlers from the site 79 | var tiddlers = extractTiddlersFromWikiFile(contents.text); 80 | if(!tiddlers) { 81 | console.log("Failed to extract tiddlers from",contents.text.slice(0,1000)); 82 | } 83 | // Look at each tiddler 84 | for(const fields of tiddlers) { 85 | const tags = parseStringArray(fields.tags || ""); 86 | // Collect tiddlers with $:/tags/Link and an "url" field that looks like an http:// or https:// URL 87 | if(tags.includes("$:/tags/Link") && fields.url && (fields.url.startsWith("https://") || fields.url.startsWith("http://"))) { 88 | const normalizedUrl = normalizeUrl(fields.url), 89 | hashedNormalizedUrl = hash(normalizedUrl), 90 | filteredTags = tags.map(tag => tag.trim()).filter(tag => tag === "$:/tags/Link" || !tag.startsWith("$:/")) 91 | .map( tag => { 92 | if(tag === "$:/tags/Link") return tag ; 93 | return tag.replace(/\w\S*/g, txt => { 94 | return txt.charAt(0).toUpperCase() + txt.substr(1).toLowerCase(); ; 95 | } 96 | 97 | ); 98 | 99 | }) 100 | .map( tag => {if( tag in topicsDict ) { return topicsDict[tag] } else { 101 | return tag ; 102 | }} 103 | ); 104 | for(const tag of filteredTags) { 105 | outputTags[tag] = true; 106 | } 107 | output.push({ 108 | title: `Link from ${siteInfo.name}: ${hashedNormalizedUrl}`, 109 | modified: fields.modified, 110 | created: fields.created, 111 | text: fields.text, 112 | url: normalizedUrl, 113 | "url-hash": hashedNormalizedUrl, 114 | tags: stringifyList(filteredTags), 115 | "original-title": fields.title, 116 | origin: siteInfo.name 117 | }) 118 | } 119 | // Collect favicons 120 | if(fields.title === "$:/favicon.ico") { 121 | output.push({ 122 | title: `$:/config/avatars/${siteInfo.name}`, 123 | name: siteInfo.name, 124 | tags: "$:/tags/Avatar", 125 | text: fields.text, 126 | type: fields.type 127 | }); 128 | } 129 | // Collect site title and subtitle 130 | if(fields.title === "$:/SiteTitle") { 131 | sourceDescriptionTiddler.caption = fields.text; 132 | } 133 | if(fields.title === "$:/SiteSubtitle") { 134 | sourceDescriptionTiddler.text = fields.text; 135 | } 136 | } 137 | // Output the source description tiddler 138 | output.push(sourceDescriptionTiddler); 139 | } 140 | } 141 | // Output the tags 142 | for(const tag in outputTags) { 143 | output.push({ 144 | title: `Topic: ${tag}`, 145 | topic: tag, 146 | tags: "$:/tags/Topic" 147 | }); 148 | } 149 | // Write the output tiddlers 150 | await mkdirAsync(path.dirname(outputTiddlersPath),{recursive: true}); 151 | await writeFileAsync(outputTiddlersPath,JSON.stringify(output,null,4),"utf8"); 152 | // Output any errors 153 | if(errors.length > 0) { 154 | console.log("Errors",errors); 155 | } 156 | } 157 | 158 | async getFileContents(url) { 159 | // Retrieve the file contents 160 | let response, text; 161 | try { 162 | response = await fetch(url); 163 | if(!response.ok) { 164 | throw response; 165 | } 166 | text = await response.text(); 167 | } catch(e) { 168 | return {error: e.toString()}; 169 | } 170 | return {text: text}; 171 | } 172 | } 173 | 174 | const app = new App(process.argv.slice(2)); 175 | 176 | app.main().then(() => { 177 | process.exit(0); 178 | }).catch(err => { 179 | console.error(err); 180 | process.exit(1); 181 | }); 182 | -------------------------------------------------------------------------------- /bin/test.js: -------------------------------------------------------------------------------- 1 | /* 2 | Run tests on sites/sites.json 3 | */ 4 | 5 | const normalizeUrl = require('normalize-url'), 6 | assert = require('assert').strict; 7 | 8 | function runTests() { 9 | const jsonSites = require("../sites/sites.json"), 10 | usernames = {}, 11 | urls = {}; 12 | assert(Array.isArray(jsonSites),"JSON object should be an array"); 13 | for(const site of jsonSites) { 14 | assert(Object.keys(site).sort().join(",") =="name,url","Each array entry should be an object {name:,url:}"); 15 | assert(typeof site.name === "string","Name must be a string"); 16 | assert(site.name.length >= 3 && site.name.length <= 32,"Name must be a string of between 3 and 32 characters"); 17 | assert(/^[a-z][a-z0-9_\-]*$/mg.test(site.name),"Name must start with a lowercase letter and only contain lower case letters, digits, dashes and underscores") 18 | assert(!(site.name in usernames),"usernames must be unique"); 19 | usernames[site.name] = true; 20 | assert(typeof site.url === "string","URL must be a string"); 21 | const normalizedUrl = normalizeUrl(site.url).toString(); 22 | assert(normalizedUrl.startsWith("http://") || normalizedUrl.startsWith("https://"),"URL must be a valid http or https URL"); 23 | assert(!(site.url in urls),"urls must be unique"); 24 | urls[site.url] = true; 25 | } 26 | } 27 | 28 | try { 29 | runTests(); 30 | } catch(e) { 31 | console.error("Error",e) 32 | process.exit(1); 33 | } 34 | process.exit(0); -------------------------------------------------------------------------------- /bin/tiddlywiki-utils.js: -------------------------------------------------------------------------------- 1 | /* 2 | TiddlyWiki Utilities 3 | */ 4 | 5 | function extractTiddlersFromWikiFile(text) { 6 | var results = []; 7 | // Check if we've got an old-style store area 8 | const storeAreaMarkerRegExp = /
/gi, 9 | storeAreaMatch = storeAreaMarkerRegExp.exec(text); 10 | if(storeAreaMatch) { 11 | // If so, it's either a classic TiddlyWiki file or an unencrypted TW5 file 12 | results.push.apply(results,deserializeTiddlyWikiFile(text,storeAreaMarkerRegExp.lastIndex,!!storeAreaMatch[1])); 13 | } 14 | // Check for new-style store areas 15 | var newStoreAreaMarkerRegExp = /