Compare commits

...

104 Commits

Author SHA1 Message Date
60294a40b9 Sanitizing YAML dictionaries. 2024-12-15 12:49:12 -05:00
6825d9f242 Removing unnecessary method. 2024-12-14 17:10:23 -05:00
738697bc63 Using other YAML prepackaged parsers. 2024-12-14 17:07:55 -05:00
d822926612 Moving functionality to improve modularity. 2024-12-13 19:06:23 -05:00
af064db451 If YAML metadata is absent, then is not shown in the text view. 2024-11-21 17:54:29 -05:00
f85ed1803b Pushing up methods. 2024-11-21 17:23:45 -05:00
92760a54a9 Minor renaming and tagging while splitting the document. 2024-11-17 12:19:39 -05:00
e73cbbb72a Improving Markdown with metadata file exportation. 2024-11-16 18:03:04 -05:00
b89b3755f7 URL in metadata needs to be commented to please Markdeep's parser. 2024-11-12 18:52:08 -05:00
d2959856b9 Preserving HedgeDoc importation metadata when converted to Markdeep. 2024-11-12 18:03:32 -05:00
ca3550a3f4 Adding default edit page time. 2024-11-04 14:26:13 -05:00
d470dd533c Fixing Markdown importation for simple cases. 2024-11-04 06:33:55 -05:00
babbd48934 Improving splitting of admonition snippets. 2024-11-04 03:28:49 -05:00
5ccff44fc9 Improving splitting of admonition strings. 2024-11-03 22:38:31 -05:00
33f3a9a0c2 Markdown crude semantic splits when converting to LePage. 2024-11-03 15:06:58 -05:00
96903d8627 Implementing Markdown crude semantic splits. 2024-11-03 14:56:57 -05:00
4beca346be Renaming and modularizing. 2024-11-03 08:59:48 -05:00
37d6000702 Implementing page "semantic splitters". 2024-11-02 18:12:46 -05:00
0ce6b89b6c Implementing page "semantic splitters". 2024-11-02 17:38:12 -05:00
629a9e32ca Alphanumeric counter facility. 2024-10-31 11:25:06 -05:00
e26334425f Merge bb7bab403e 2024-10-22 13:43:02 -05:00
58ec9eed76 Fixing metadata retention between HedgeDoc and Lepiter. 2024-10-22 13:35:24 -05:00
bb7bab403e File metadata extraction. 2024-10-14 13:01:27 -05:00
cea7ce5e6c Merge pull request 'Reclasifying.' (!6) from unsync into master
Reviewed-on: #6
2024-10-14 15:35:47 +00:00
65fb92964f Reclasifying. 2024-10-14 10:34:35 -05:00
cccf81e89b Merge branch 'master' of https://code.sustrato.red/Offray/MiniDocs 2024-09-25 17:40:32 -05:00
fa6303b762 Creating short titles, when titles are too long. 2024-09-25 17:17:34 -05:00
02403b5ae3 Migrating Pandoc conversors. 2024-09-24 13:27:51 -05:00
e4d0880fea Migrating Pandoc conversors. 2024-09-24 13:26:26 -05:00
ee80105f44 Migrating Pandoc conversors. 2024-09-24 13:24:16 -05:00
ecb4321551 Debugging YAML metadata detection. 2024-09-24 13:17:59 -05:00
56ef3869ca Improving setters. 2024-09-24 12:23:39 -05:00
8fe49106bb Debugging page importation. 2024-08-21 12:10:03 -05:00
ff9fbb92f9 Debugging page importation. 2024-08-21 11:54:25 -05:00
a6e49448fa Debugging page importation via snippet. 2024-08-21 11:26:24 -05:00
45c4762201 Debugging importation. 2024-08-21 11:19:03 -05:00
8634047be9 Debuggin picture importation code. 2024-08-21 11:12:28 -05:00
f3929ceece Improving importation code. 2024-08-21 11:02:21 -05:00
b4d6940ec6 Fixing importing LePicture bug caused by new announcers behavior. 2024-08-21 09:06:39 -05:00
7bbf399ae1 Improving page importation code. 2024-08-21 08:33:34 -05:00
fb64d5c1ae Improving management of empty YAML metadata. 2024-08-02 12:35:21 -05:00
554fb9000e Starting interface with Taskwarrior. It may become its own repository later. 2024-07-28 10:57:59 -05:00
47eaafaf5c Fixing baseline. 2024-07-23 01:18:10 +00:00
78900cc64d Solving sync conflicts. 2024-07-22 19:34:46 -05:00
78f1b1474f Improving exportation. 2024-07-22 19:24:56 -05:00
94fdf3052f Modularizing and cleaning to improve upcoming support for tagged snippets. 2024-07-18 17:08:46 -05:00
6072fd8971 Retaking taging as a way to improve document export/import. 2024-07-18 16:26:32 -05:00
6e58c5631d Fixing Lepiter Building Blocs. 2024-07-17 14:45:22 -05:00
8cadf004dd Fixing typo in baseline. testing for Lepiter building blocs dependency. 2024-07-17 14:40:17 -05:00
2bc0b44fa2 Modifying baseline for testing with Lepiter Building Blocs dependency. 2024-07-17 14:37:15 -05:00
882f33859c Web preview for pages not located in the default Lepiter folder. 2024-06-20 12:15:31 -05:00
968bfff3bb Ignoring importation errors, temporarily. 2024-06-20 11:10:56 -05:00
f67a24c94c Ignoring importation errors, temporarily. 2024-06-20 10:40:37 -05:00
3c01731fdc Debuging new importation errors derived, probably, from coder updating strategies. 2024-06-20 10:32:17 -05:00
1b66316d03 More modular code. Pending! Redundancies should be revised. 2024-06-20 10:12:05 -05:00
62a5c398d1 Adding characters to accented correction. 2024-06-19 18:33:07 -05:00
23335a7727 Debugging metadata export/import. 2024-06-11 16:35:29 -05:00
3202717cea Debugging metadata exportation. 2024-06-11 16:12:05 -05:00
27a28ce543 Debugging metadata exportation. 2024-06-11 16:09:11 -05:00
47aabb1d4f Enriching object metadata during exportation. 2024-06-11 15:43:23 -05:00
eeaf28127b Adding Markdeep export to GraphiQL snippets. 2024-05-25 20:56:50 -05:00
141942ce3e Merge c9b214f633 2024-05-21 11:37:09 -05:00
c9b214f633 Creating extension for ZnConstants. 2024-05-21 11:37:31 -05:00
59b8387728 Extending exportation to other formats. 2024-05-21 11:36:46 -05:00
6433da508a Unique ids for booleans in values and improved visualization. 2024-05-11 12:19:34 -05:00
5cf1d7bcc6 Unique ids for nils and booleans in arrays. 2024-05-11 11:51:20 -05:00
28a3d22911 Utilities for visualizing dictionaries. 2024-05-11 11:19:25 -05:00
da459b9d26 Debugging LogSeq pages. 2024-05-03 17:20:52 -05:00
31e27acdff Implementing FileLocator aliases, so data narratives can use them instead of fixed locations. 2024-05-02 16:00:57 -05:00
Offray
f592e29eef Moving NanoID to ExoRepo. 2024-04-30 16:51:32 -05:00
Offray
de61e736fa Moving YQ to ExoRepo and adding Windows Scoop path. 2024-04-30 16:24:27 -05:00
a22005da27 Starting multiOS support for binary files. 2024-04-29 17:28:20 -05:00
5e4db00352 Fixing email String importation/preview. 2024-04-28 14:43:07 -05:00
ef982eb6a1 Nim's package manager, Nimble, moved from MiniDocs to ExoRepo. 2024-04-27 03:59:21 -05:00
089eb8c2eb Fixing image link importation for descriptions with links inside. 2024-04-26 08:48:53 -05:00
5ec6ea6377 Arreglando manualmente problema en el baseline. 2024-04-25 12:00:19 -05:00
7dc0bdaac0 Debugging image links with links in the description. 2024-04-22 14:59:56 -05:00
0c45ccb39e Debugging image links with links in the description. 2024-04-22 14:01:59 -05:00
573c929845 Improving LeSnippet to LePage conversion. 2024-04-16 14:18:41 -05:00
13f9b8050e Fixing repeated medatada when exporting to Markdeep. 2024-04-13 11:49:04 -05:00
53910fa840 Recovering metadata from children for updating notebook metadata. 2024-04-13 10:00:12 -05:00
bf0ea4b46d Recovering headers. 2024-04-12 20:27:21 -05:00
977922d7a3 Enabling importation of Markdeep pages without page metadata. 2024-04-11 13:06:21 -05:00
51e84c2404 Markdown can be exported with or without metadata wrappers. 2024-04-09 16:03:47 -05:00
8fb6373a83 Refactoring, so functionality can be called without the GUI. 2024-04-08 12:46:42 -05:00
9ae7a6ec62 Adding Nim and NanoID support to Windows platform. 2024-04-04 17:35:25 -05:00
7979007091 Merge a264070d5c 2024-04-03 17:27:09 -05:00
a264070d5c Merge 3a2d096025 2024-04-03 17:10:18 -05:00
642712cdfd Adding yq support to Windows platform. 2024-04-03 17:10:01 -05:00
34d77ecedd Default Markdown and webView for DataFrames. 2024-04-03 10:13:19 -05:00
3a2d096025 Debugging baseline, comment on LepiterBuildingBlocs. 2024-04-02 23:51:36 +00:00
7cd3f30216 Debugging baseline, fixing typo. 2024-04-02 23:42:34 +00:00
5c897886e0 Debugging baseline, error handler for LepiterBuildingBlocs. 2024-04-02 23:32:41 +00:00
2037af37a3 Debugging baseline, adding dataframe. 2024-04-02 23:21:15 +00:00
82405165e1 Debugging baseline. 2024-04-02 23:11:47 +00:00
b8262d00ca Debugging baseline. 2024-04-02 22:36:49 +00:00
6832d9024d Default Markdown and webView for DataFrames. 2024-04-01 13:17:59 -05:00
da4c9bf9c4 Recovering from unexplainable detached repository head. 2024-03-30 10:17:53 -05:00
2ca24978bc Arreglando manualmente problema en el baseline. 2024-03-30 10:11:42 -05:00
bba3daebfa Quitando archivo en conflicto antes del merge. 2024-03-30 10:07:07 -05:00
0c5ede8498 DataFrame as a dependency. Improved dependencies reading, as they are getting longer. 2024-03-29 10:38:27 -05:00
87fba41704 Merge b2a4dc1839 2024-03-29 10:33:11 -05:00
0305a68aca DataFrame as a dependency. 2024-03-29 10:28:57 -05:00
b2a4dc1839 Debugging baseline. 2024-03-29 00:01:24 -05:00
29 changed files with 710 additions and 358 deletions

View File

@ -13,8 +13,7 @@ BaselineOfMiniDocs >> baseline: spec [
"Dependencies"
self setUpTeapot: spec.
self setUpPetitParser: spec.
"LepiterBuildingBlocs commented while resolving the conflict with the internal gtoolkit renaming."
"self setUpLepiterBuildingBlocs: spec"
self setUpLepiterBuildingBlocs: spec. "working in v1.0.993"
spec
baseline: 'Mustache' with: [ spec repository: 'github://noha/mustache' ];
baseline: 'Temple' with: [ spec repository: 'github://astares/Pharo-Temple/src' ];
@ -32,7 +31,8 @@ BaselineOfMiniDocs >> baseline: spec [
'Mustache' 'Temple' "Templating"
'Teapot' 'Tealight' "Web server"
'PetitMarkdown' 'PetitParser' "Parsers"
'DataFrame' "Tabular data")].
'DataFrame' "Tabular data"
'LepiterBuildingBlocs' "Lepiter utilities")].
.
"Groups"
@ -60,10 +60,7 @@ BaselineOfMiniDocs >> semanticVersion [
BaselineOfMiniDocs >> setUpLepiterBuildingBlocs: spec [
spec
baseline: 'LepiterBuildingBlocs'
with: [spec
repository: 'github://botwhytho/LepiterBuildingBlocs:main/src';
loads: #('ALL')];
import: 'LepiterBuildingBlocs'
with: [spec repository: 'github://botwhytho/LepiterBuildingBlocs:main/src']
]
{ #category : #accessing }

View File

@ -0,0 +1,18 @@
"
I model a possible bridge between TaskWarrior and MiniDocs. (starting DRAFT).
"
Class {
#name : #AcroReport,
#superclass : #Object,
#category : #MiniDocs
}
{ #category : #accessing }
AcroReport class >> project: projectName [
| jsonReport |
jsonReport := (GtSubprocessWithInMemoryOutput new
shellCommand: 'task project:', projectName , ' export';
runAndWait;
stdout).
^ STONJSON fromString: jsonReport
]

View File

@ -0,0 +1,57 @@
Class {
#name : #AlphanumCounter,
#superclass : #Object,
#instVars : [
'letters',
'digits',
'currentLetter',
'currentDigit'
],
#category : #MiniDocs
}
{ #category : #accessing }
AlphanumCounter >> current [
^ self currentLetter asString, self currentDigit asString
]
{ #category : #accessing }
AlphanumCounter >> currentDigit [
^ currentDigit ifNil: [ currentDigit := self digits first ]
]
{ #category : #accessing }
AlphanumCounter >> currentLetter [
^ currentLetter ifNil: [ currentLetter := self letters first ]
]
{ #category : #accessing }
AlphanumCounter >> currentLetterIndex [
^ self letters detectIndex: [:n | n = self currentLetter]
]
{ #category : #accessing }
AlphanumCounter >> digits [
^ digits ifNil: [ digits := 1 to: 9 ]
]
{ #category : #accessing }
AlphanumCounter >> digits: aNumbersArray [
digits := aNumbersArray
]
{ #category : #accessing }
AlphanumCounter >> increase [
(self currentDigit < self digits last)
ifTrue: [ currentDigit := currentDigit + 1 ]
ifFalse: [
currentLetter := self letters at: (self currentLetterIndex + 1).
currentDigit := self digits first
]
]
{ #category : #accessing }
AlphanumCounter >> letters [
^ letters ifNil: [ letters := $A to: $Z ]
]

View File

@ -26,3 +26,20 @@ Array >> bagOfWordsFor: sentenceArray [
].
^ bagOfWords
]
{ #category : #'*MiniDocs' }
Array >> replaceWithUniqueNilsAndBooleans [
| response |
(self includesAny: #(true false nil))
ifFalse: [ response := self ]
ifTrue: [ | newItem |
response := OrderedCollection new.
self do: [:item |
(item isBoolean or: [ item isNil ])
ifTrue: [ newItem := item asString, '-', (NanoID generate copyFrom: 1 to: 3) ]
ifFalse: [ newItem := item ].
response add: newItem.
].
].
^ response
]

View File

@ -0,0 +1,12 @@
Extension { #name : #ByteString }
{ #category : #'*MiniDocs' }
ByteString >> asHTMLComment [
^ '<!-- ', self , ' -->'
]
{ #category : #'*MiniDocs' }
ByteString >> email [
"Quick fix for importing Lepiter pages that have a plain ByteString field as email."
^ self
]

View File

@ -0,0 +1,6 @@
Extension { #name : #Dictionary }
{ #category : #'*MiniDocs' }
Dictionary >> treeView [
^ self asOrderedDictionary treeView
]

View File

@ -0,0 +1,53 @@
Extension { #name : #FileLocator }
{ #category : #'*MiniDocs' }
FileLocator class >> aliases [
| fileAliases |
fileAliases := self fileAliases.
fileAliases exists
ifFalse: [ | initialConfig |
initialConfig := Dictionary new.
fileAliases ensureCreateFile.
MarkupFile exportAsFileOn: fileAliases containing: initialConfig
].
^ STON fromString: fileAliases contents
]
{ #category : #'*MiniDocs' }
FileLocator class >> atAlias: aString put: aFolderOrFile [
| updatedAliases |
updatedAliases:= self aliases
at: aString put: aFolderOrFile;
yourself.
MarkupFile exportAsFileOn: self fileAliases containing: updatedAliases.
^ updatedAliases
]
{ #category : #'*MiniDocs' }
FileLocator >> extractMetadata [
"I package the functionality from [[How to extract meta information using ExifTool]],
from the GToolkit Book.
I depend on the external tool ExifTool."
| process variablesList |
process := GtSubprocessWithInMemoryOutput new
command: 'exiftool';
arguments: { self fullName}.
process errorBlock: [ :proc | ^ self error: 'Failed to run exiftool' ].
process runAndWait.
variablesList := process stdout lines collect: [ :currentLine |
| separatorIndex name value |
separatorIndex := currentLine indexOf: $:.
name := (currentLine copyFrom: 1 to: separatorIndex - 1) trimBoth.
value := (currentLine
copyFrom: separatorIndex + 1
to: currentLine size) trimBoth.
name -> value
].
^ variablesList asOrderedDictionary
]
{ #category : #'*MiniDocs' }
FileLocator class >> fileAliases [
^ MiniDocs appFolder / 'fileAliases.ston'
]

View File

@ -1,5 +1,51 @@
Extension { #name : #GtGQLSnippet }
{ #category : #'*MiniDocs' }
GtGQLSnippet >> asMarkdeep [
| output |
output := WriteStream on: ''.
(self metadata)
at: 'operation' put: self operation;
at: 'input' put: self input;
at: 'context' put: self context;
yourself.
output
nextPutAll: self metadataDiv;
nextPutAll: self markdeepCustomOpener;
nextPutAll: self asMarkdownString;
nextPut: Character lf;
nextPutAll: self markdeepCustomCloser;
nextPut: Character lf;
nextPutAll: '</div>';
nextPut: Character lf;
nextPut: Character lf.
^ output contents withInternetLineEndings
]
{ #category : #'*MiniDocs' }
GtGQLSnippet >> markdeepCustomCloser [
^ self markdeepCustomOpener
]
{ #category : #'*MiniDocs' }
GtGQLSnippet >> markdeepCustomOpener [
^ '* * *'
]
{ #category : #'*MiniDocs' }
GtGQLSnippet >> metadataDiv [
"PENDING: Shared among several snippets. Should be abstracted further?"
| output |
output := WriteStream on: ''.
output
nextPutAll: '<div st-class="' , self class greaseString , '"';
nextPut: Character lf;
nextPutAll: ' st-data="' , (STON toStringPretty: self metadata) , '">';
nextPut: Character lf.
^ output contents withInternetLineEndings.
]
{ #category : #'*MiniDocs' }
GtGQLSnippet >> metadataUpdate [
| createEmailSanitized editEmailSanitized |
@ -7,7 +53,7 @@ GtGQLSnippet >> metadataUpdate [
editEmailSanitized := self editEmail asString withoutXMLTagDelimiters.
^ OrderedDictionary new
at: 'id' put: self uidString;
at: 'parent' put: self parent uuid;
at: 'parent' put: self parent uid asString36;
at: 'created' put: self createTime asString;
at: 'modified' put: self latestEditTime asString;
at: 'creator' put: createEmailSanitized;

View File

@ -29,15 +29,36 @@ HedgeDoc class >> newDefault [
{ #category : #accessing }
HedgeDoc >> asLePage [
| newPage snippet |
snippet := LeTextSnippet new
string: self bodyWithoutTitleHeader promoteMarkdownHeaders.
| newPage sanitizedMarkdown |
sanitizedMarkdown := self bodyWithoutTitleHeader promoteMarkdownHeaders.
newPage := LePage new
initializeTitle: self title;
addSnippet: snippet;
yourself.
newPage incomingLinks.
newPage metadata addAll: self metadata.
initializeTitle: self title.
sanitizedMarkdown := sanitizedMarkdown markdownSplitted.
sanitizedMarkdown class = OrderedCollection ifTrue: [
sanitizedMarkdown do: [:lines | | snippet |
snippet := LeTextSnippet new
string: lines asStringWithCr;
uid: LeUID new.
newPage
addSnippet: snippet;
yourself
]
].
sanitizedMarkdown class = ByteString ifTrue: [ | snippet |
snippet := LeTextSnippet new
string: sanitizedMarkdown;
uid: LeUID new.
newPage
addSnippet: snippet;
yourself
].
newPage
incomingLinks;
splitAdmonitionSnippets.
newPage editTime: DateAndTime now.
newPage options
at: 'HedgeDoc' at: 'yamlFrontmatter' put: self metadata;
at: 'HedgeDoc' at: 'url' put: self url asString asHTMLComment.
^ newPage
]
@ -151,7 +172,7 @@ HedgeDoc >> server: aUrlString [
{ #category : #accessing }
HedgeDoc >> url [
^ url asUrl
url ifNotNil: [ ^ url asUrl ]
]
{ #category : #accessing }
@ -162,7 +183,6 @@ HedgeDoc >> url: anObject [
(html xpath: '//head/meta[@name="application-name"][@content = "HedgeDoc - Ideas grow better together"]') isEmpty
ifTrue: [ self inform: 'Not a hedgedoc url'.
url := nil ].
self metadata at: 'title' put: tempUrl firstPathSegment.
server := tempUrl host.
url := anObject
]

View File

@ -37,40 +37,39 @@ LeDatabase >> addPageCopy: aLePage [
{ #category : #'*MiniDocs' }
LeDatabase >> addPageFromMarkdeep: markdeepDocTree withRemote: externalDocLocation [
| remoteMetadata divSnippets dataSnippets snippets page |
| remoteMetadata divSnippets dataSnippets page |
divSnippets := (markdeepDocTree xpath: '//div[@st-class]') asOrderedCollection
collect: [ :xmlElement | xmlElement postCopy ].
remoteMetadata := Markdeep new metadataFromXML: markdeepDocTree.
"Ensuring remote metadata has consistent data"
remoteMetadata at: 'origin' put: externalDocLocation.
remoteMetadata at: 'title' ifAbsentPut: [ markdeepDocTree detectMarkdeepTitle ].
remoteMetadata at: 'id' ifAbsentPut: [UUID new asString36].
remoteMetadata at: 'created' ifAbsentPut: [ DateAndTime now] .
remoteMetadata at: 'creator' ifAbsentPut: [ 'unknown' ].
remoteMetadata at: 'modified' ifAbsentPut: [ DateAndTime now].
remoteMetadata at: 'modifier' ifAbsentPut: [ 'unknown' ].
dataSnippets := self sanitizeMarkdeepSnippets: divSnippets withMetadata: remoteMetadata.
snippets := dataSnippets collect: [ :each | each asLepiterSnippet ].
page := LePage new.
page
title: (remoteMetadata at: 'title' ifAbsent: [ page detectMarkdeepTitleFrom: markdeepDocTree ]);
basicUid: (UUID fromString36: (remoteMetadata at: 'id' ifAbsent: [UUID new asString36]));
createTime: (LeTime new
time: (remoteMetadata at: 'created' ifAbsent: [ DateAndTime now]) asDateAndTime);
editTime: (LeTime new
time: (remoteMetadata at: 'modified' ifAbsent: [ DateAndTime now]) asDateAndTime);
latestEditTime: (LeTime new
time: (remoteMetadata at: 'modified' ifAbsent: [ DateAndTime now]) asDateAndTime);
createEmail: (remoteMetadata at: 'creator' ifAbsent: [ 'unknown' ]);
editEmail: (remoteMetadata at: 'modifier' ifAbsent: [ 'unknown' ]).
snippets do: [ :snippet | "| currentParent |"
page fromDictionary: remoteMetadata.
dataSnippets do: [:each | | snippet|
snippet := each asLepiterSnippet.
page addSnippet: snippet.
"currentParent := page detectParentSnippetWithUid: (snippet metadata at: 'parent').
snippet parent: currentParent."
].
page children
do: [ :snippet |
(self hasBlockUID: snippet uid)
ifTrue: [ | existingPage |
existingPage := self pages
detect: [ :pageTemp | pageTemp includesSnippetUid: snippet uid ].
self importErrorForLocal: existingPage withRemote: externalDocLocation.
^ self ]
ifFalse: [ snippet database: self.
self registerSnippet: snippet ] ].
detect: [ :pageTemp | pageTemp includesSnippetUid: snippet uid ]
ifFound: [
self importErrorForLocal: existingPage withRemote: externalDocLocation.
^ self
]
ifNone: [ snippet database: self ].
]
ifFalse: [ snippet database: self ]
].
self addPage: page.
^ page
]

View File

@ -3,7 +3,7 @@ Extension { #name : #LePage }
{ #category : #'*MiniDocs' }
LePage >> asHtmlFile [
self asMarkdownFile.
self asMarkdownFileWithMetadataWrappers.
self defaultPandocTemplate exists
ifFalse: [ MarkupFile installTemplate: 'https://mutabit.com/repos.fossil/mutabit/doc/trunk/plantillas/Pandoc/clean-menu-mod.html' into: self defaultPandocTemplate parent ].
@ -21,6 +21,7 @@ LePage >> asHtmlFile [
LePage >> asMarkdeep [
| bodyStream markdeep |
bodyStream := '' writeStream.
bodyStream nextPutAll: self notebookMetadataSnippet asMarkdeep.
self preorderTraversal
do: [ :snippet | bodyStream nextPutAll: snippet asMarkdeep ].
markdeep := Markdeep new
@ -47,6 +48,7 @@ LePage >> asMarkdeepFile [
{ #category : #'*MiniDocs' }
LePage >> asMarkdown [
"PENDING: to debug the output."
| bodyStream markdown |
bodyStream := '' writeStream.
bodyStream
@ -54,13 +56,13 @@ LePage >> asMarkdown [
self preorderTraversal
do: [ :snippet | bodyStream nextPutAll: snippet asMarkdown ].
markdown := Markdown new
contents: bodyStream contents demoteMarkdownHeaders;
contents: bodyStream contents promoteMarkdownHeaders;
metadata: (self metadata at: 'original' ifAbsentPut: Dictionary new).
^ markdown
]
{ #category : #'*MiniDocs' }
LePage >> asMarkdownFile [
LePage >> asMarkdownFileWithMetadataWrappers [
| folder |
folder := self storage.
^ MarkupFile exportAsFileOn: folder / self markdownFileName containing: self asMarkdownWithMetadataWrappers contents
@ -70,13 +72,15 @@ LePage >> asMarkdownFile [
LePage >> asMarkdownWithMetadataWrappers [
| bodyStream markdown |
bodyStream := '' writeStream.
bodyStream
nextPutAll: '# ', self title; cr; cr.
"bodyStream
nextPut: Character lf;
nextPutAll: '# ', self title; cr; cr."
self preorderTraversal
do: [ :snippet | bodyStream nextPutAll: snippet asMarkdownWithMetadataWrappers ].
markdown := Markdown new
contents: bodyStream contents demoteMarkdownHeaders;
metadata: (self metadata at: 'original' ifAbsentPut: Dictionary new).
contents: bodyStream contents promoteMarkdownHeaders;
title: self title;
metadata: self metadata.
^ markdown
]
@ -95,14 +99,6 @@ LePage >> defaultPandocTemplate [
^ FileLocator home / '.pandoc' / 'templates' / 'clean-menu-mod.html'
]
{ #category : #'*MiniDocs' }
LePage >> detectMarkdeepTitleFrom: xmlSubtree [
| titleLine |
titleLine := (xmlSubtree nodesCollect: [:node | node contentString ]) first lines
detect: [:line | line includesSubstring: ' **'] ifNone: ['Untitled'].
^ titleLine trimmed trimBoth: [:char | char = $* ]
]
{ #category : #'*MiniDocs' }
LePage >> detectParentSnippetWithUid: uidString [
uidString = self uid asString36 ifTrue: [ ^ self ].
@ -124,11 +120,33 @@ LePage >> exportMetadataToHead: markdeep [
{ #category : #'*MiniDocs' }
LePage >> exportedFileName [
| sanitized |
sanitized := self title asDashedLowercase romanizeAccents copyWithoutAll: #($/ $: $🢒).
| sanitized titleWords shortTitle |
titleWords := self title splitOn: Character space.
(titleWords size > 11)
ifTrue: [
titleWords := titleWords copyFrom: 1 to: 3.
shortTitle := titleWords joinUsing: Character space.
]
ifFalse: [shortTitle := self title].
sanitized := shortTitle asDashedLowercase romanizeAccents copyWithoutAll: #($/ $: $🢒 $,).
^ sanitized , '--' , (self uidString copyFrom: 1 to: 5)
]
{ #category : #'*MiniDocs' }
LePage >> fromDictionary: aDictionary [
self
title: (aDictionary at: 'title');
basicUid: (UUID fromString36: (aDictionary at: 'id'));
createTime: (LeTime new
time: (aDictionary at: 'created') asDateAndTime);
editTime: (LeTime new
time: (aDictionary at: 'modified') asDateAndTime);
latestEditTime: (LeTime new
time: (aDictionary at: 'modified') asDateAndTime);
createEmail: (aDictionary at: 'creator');
editEmail: (aDictionary at: 'modifier').
]
{ #category : #'*MiniDocs' }
LePage >> fromMarkdeepUrl: aString [
| docTree pageMetadata |
@ -162,7 +180,10 @@ LePage >> latestEditTime: aLeTime [
LePage >> localHostAddress [
| localUrl route |
MiniDocsServer teapot server isRunning ifFalse: [ MiniDocsServer restart ].
route := MiniDocsServer teapot staticRouter prefix joinUsing: '/'.
route := self storage path segments joinUsing: '/'.
MiniDocsServer teapot
serveStatic: ('/', route, '/', self markdeepFileName)
from: self storage / self markdeepFileName.
localUrl := MiniDocsServer teapot server localUrl asString.
^ localUrl, route, '/', self markdeepFileName
]
@ -206,6 +227,17 @@ LePage >> navTop [
ifTrue: [ ^ topNavFile contents ]
]
{ #category : #'*MiniDocs' }
LePage >> notebookMetadataSnippet [
| response |
response := LeTextSnippet new fromString: '<!-- See this snippet source code for this notebook''s metadata -->'.
response parent: self.
self optionAt: 'HedgeDoc' ifAbsent: [ ^ response ].
(response extra)
at: 'HedgeDoc' put: (self optionAt: 'HedgeDoc').
^ response
]
{ #category : #'*MiniDocs' }
LePage >> olderChild [
"I provide the last edited child node.
@ -265,6 +297,25 @@ LePage >> sharedVariablesBindings [
^ shared asDictionary
]
{ #category : #'*MiniDocs' }
LePage >> splitAdmonitionSnippets [
"I'm used to clean after importing from HedgeDoc to ensure that a snippet contains only admonitions and extra content is put in a new cell."
| admonitionSnippets |
admonitionSnippets := self children select: [:node | node string startsWithMarkdownAdmonition ].
admonitionSnippets ifEmpty: [ ^ self ].
admonitionSnippets do: [:node | | nodeContent |
node ifNotNil: [
nodeContent := node string.
nodeContent startsWithMarkdownAdmonition
ifTrue: [ | snippetCommand |
snippetCommand := node splitSnippetCommandAtPosition: nodeContent admonitionEndingPosition.
snippetCommand execute.
node tagWith: (nodeContent lines first trimBoth withoutPrefix: ':::')
]
]
]
]
{ #category : #'*MiniDocs' }
LePage >> storage [
| current |

View File

@ -8,6 +8,17 @@ LePharoSnippet >> contentAsStringCustomized [
^ thisObject perform: self detectMessage trimmed asSymbol.
]
{ #category : #'*MiniDocs' }
LePharoSnippet >> fromDictionary: anOrderedDictionary [
self
uid: (LeUID new uidString: (anOrderedDictionary at: 'id'));
parent: (anOrderedDictionary at: 'parent');
createTime: (LeTime new time: ((anOrderedDictionary at: 'created')asDateAndTime));
editTime: (LeTime new time: ((anOrderedDictionary at: 'modified') asDateAndTime));
editEmail: (anOrderedDictionary at: 'modifier');
createEmail: (anOrderedDictionary at: 'creator').
]
{ #category : #'*MiniDocs' }
LePharoSnippet >> fromMarkdeep: markdeepDiv [
@ -17,7 +28,7 @@ LePharoSnippet >> fromMarkdeep: markdeepDiv [
{ #category : #'*MiniDocs' }
LePharoSnippet >> fromString: aString [
self code: aString
[ self coder forSource: aString ] onErrorDo: [ ]
]
{ #category : #'*MiniDocs' }

View File

@ -41,22 +41,37 @@ LePictureSnippet >> contentFrom: markdeepDiv [
self urlString: (markdeepDiv // 'img' @ 'src') stringValue.
]
{ #category : #'*MiniDocs' }
LePictureSnippet >> fromDictionary: anOrderedDictionary [
| sanitizedUrl|
sanitizedUrl := (anOrderedDictionary at: 'url').
sanitizedUrl := sanitizedUrl copyFrom: 5 to: sanitizedUrl size - 3.
self
uid: (LeUID new uidString: (anOrderedDictionary at: 'id'));
parent: (anOrderedDictionary at: 'parent');
createTime: (LeTime new time: ((anOrderedDictionary at: 'created')asDateAndTime));
editTime: (LeTime new time: ((anOrderedDictionary at: 'modified') asDateAndTime));
editEmail: (anOrderedDictionary at: 'modifier');
createEmail: (anOrderedDictionary at: 'creator');
urlString: sanitizedUrl;
caption: (anOrderedDictionary at: 'content') first
]
{ #category : #'*MiniDocs' }
LePictureSnippet >> fromMarkdeep: markdeepDiv [
^ markdeepDiv asSnippetDictionary asLepiterSnippet
]
{ #category : #'*MiniDocs' }
LePictureSnippet >> fromString: aString [
"aString should be a valid Markdown/Markdeep image string"
| args captionTemp urlTemp |
LePictureSnippet >> fromString: aStringArray [
"aStringArray should contain as first element the sanitized string and
as second the full original image Link string, which may contains links in the description."
| args urlTemp |
args := aString splitOn: ']('.
captionTemp := args first.
captionTemp := captionTemp copyFrom: 3 to: captionTemp size.
urlTemp := args second.
args := aStringArray second splitOn: ']('.
urlTemp := args last.
urlTemp := urlTemp copyFrom: 1 to: urlTemp size - 1.
self caption: captionTemp.
self caption: aStringArray first.
self urlString: urlTemp.
^ self
]

View File

@ -13,7 +13,18 @@ LeSnippet class >> fromMetaMarkdeep: div [
{ #category : #'*MiniDocs' }
LeSnippet >> metadata [
^ self metadataUpdate
| createEmailSanitized editEmailSanitized |
createEmailSanitized := self createEmail asString withoutXMLTagDelimiters.
editEmailSanitized := self editEmail asString withoutXMLTagDelimiters.
self optionAt: 'metadata' ifAbsentPut: [ OrderedDictionary new ].
^ (self optionAt: 'metadata')
at: 'id' put: self uidString;
at: 'parent' put: self parent uid asString36;
at: 'created' put: self createTime asString;
at: 'modified' put: self latestEditTime asString;
at: 'creator' put: createEmailSanitized;
at: 'modifier' put: editEmailSanitized;
yourself
]
{ #category : #'*MiniDocs' }

View File

@ -1,5 +1,25 @@
Extension { #name : #LeTextCoderSnippetElement }
{ #category : #'*MiniDocs' }
LeTextCoderSnippetElement >> asLePage [
| currentSnippet newPage |
currentSnippet := self snippet.
newPage := LePage new.
newPage
title: (currentSnippet text asString trimLeft: [:char | char = $# ]) trim.
self page database
addPage: newPage.
currentSnippet allChildrenBreadthFirstDo: [:child |
child moveToPageTitled: newPage title.
].
^ newPage
]
{ #category : #'*MiniDocs' }
LeTextCoderSnippetElement >> asSnippetViewModel [
^ self snippetContent
]
{ #category : #'*MiniDocs' }
LeTextCoderSnippetElement >> moveToPageTitled: pageName [
| db origin destination |

View File

@ -18,6 +18,17 @@ LeTextSnippet >> asLePage [
^ page.
]
{ #category : #'*MiniDocs' }
LeTextSnippet >> fromDictionary: anOrderedDictionary [
self
uid: (LeUID new uidString: (anOrderedDictionary at: 'id'));
parent: (anOrderedDictionary at: 'parent');
createTime: (LeTime new time: ((anOrderedDictionary at: 'created')asDateAndTime));
editTime: (LeTime new time: ((anOrderedDictionary at: 'modified') asDateAndTime));
editEmail: (anOrderedDictionary at: 'modifier');
createEmail: (anOrderedDictionary at: 'creator')
]
{ #category : #'*MiniDocs' }
LeTextSnippet >> fromMarkdeep: markdeepDiv [
@ -27,7 +38,9 @@ LeTextSnippet >> fromMarkdeep: markdeepDiv [
{ #category : #'*MiniDocs' }
LeTextSnippet >> fromString: aString [
self string: aString
self
string: aString;
uid: LeUID new.
]
{ #category : #'*MiniDocs' }
@ -49,7 +62,18 @@ LeTextSnippet >> parentId [
]
{ #category : #'*MiniDocs' }
LeTextSnippet >> taggedWith: aString [
self metadata at: 'tags' ifPresent: [ (self metadata at: 'tags') add: aString; yourself ] ifAbsentPut: [ Set new ].
^ self metadata at: 'tags'
LeTextSnippet >> tagWith: aString [
self tags add: aString.
]
{ #category : #'*MiniDocs' }
LeTextSnippet >> withFollowingSnippets [
"I'm the same method implemented for PharoSnippets,
but present also here as a way to improve moving prose snippets from pages.
"
| snippets stop start |
snippets := self parent children asArray.
start := snippets indexOf: self.
stop := snippets size.
^ snippets copyFrom: start to: stop
]

View File

@ -2,16 +2,11 @@ Extension { #name : #LeTextualSnippet }
{ #category : #'*MiniDocs' }
LeTextualSnippet >> asMarkdeep [
"Inspired by Alpine.js and Assembler CSS 'x-' properties, we are going to use
'st-' properties as a way to extend divs metadata regarding its contents."
| output |
output := WriteStream on: ''.
output
nextPutAll: '<div st-class="' , self class greaseString , '"';
nextPut: Character lf;
nextPutAll: ' st-data="' , (STON toStringPretty: self metadata) , '">';
nextPut: Character lf;
nextPutAll: self metadataDiv;
nextPutAll: self markdeepCustomOpener;
nextPutAll: self contentAsStringAnnotated;
nextPut: Character lf;
@ -39,44 +34,18 @@ LeTextualSnippet >> asMarkdownWithMetadataWrappers [
| output |
output := '' writeStream.
output
nextPutAll: '<div st-class="', self class asString, '"'; lf;
nextPutAll: ' st-data="', (STON toString: self metadata), '">'; lf;
nextPutAll: self metadataDiv;
nextPutAll: self markdownCustomOpener;
nextPutAll: self contentAsStringCustomized; lf;
nextPutAll: self markdownCustomCloser;
nextPutAll: '</div>'; lf; lf.
^ output contents
^ output contents withInternetLineEndings
]
{ #category : #'*MiniDocs' }
LeTextualSnippet >> contentAsStringAnnotated [
| annotations substitutions exported pageConfig|
self ast ifNil: [ ^ self contentAsString ].
annotations := self ast parts select: [:each | each className includesSubstring: 'AnnotationNode' ].
annotations ifEmpty: [ ^ self contentAsString ].
substitutions := OrderedDictionary new.
pageConfig := self page config.
annotations do: [ :each | | key type value color |
key := each source.
type := (key splitOn: ':') first copyWithoutAll: '{{'.
value := key copyFrom: type size + 4 to: key size - 2.
pageConfig
ifNil: [ color := 'default' ]
ifNotNil: [ | colors |
colors := pageConfig at: 'annotationColors' ifAbsent: [ nil ].
colors ifNotNil: [
color := colors at: type ifAbsent: [ colors at: 'defaultColor' ifAbsentPut: ['default'] ]
]
].
substitutions
at: key
put: '<span st-class="',type,'" style="color:', color, '">', value,'</span>'.
].
exported := self contentAsString.
substitutions keysAndValuesDo: [:k :v |
exported := exported copyReplaceAll: k with: v.
].
^ exported
self ast ifNotNil: [ ^ self processSnippetAnnotations ].
^ self contentAsString
]
{ #category : #'*MiniDocs' }
@ -86,6 +55,11 @@ LeTextualSnippet >> contentAsStringCustomized [
ifFalse: [ ^ self contentAsString ]
]
{ #category : #'*MiniDocs' }
LeTextualSnippet >> extra [
^ self optionAt: 'extra' ifAbsentPut: [ Dictionary new ]
]
{ #category : #'*MiniDocs' }
LeTextualSnippet >> markdeepCustomCloser [
^ ''
@ -112,6 +86,21 @@ LeTextualSnippet >> metadata [
^ self metadataUpdate
]
{ #category : #'*MiniDocs' }
LeTextualSnippet >> metadataDiv [
"Inspired by Alpine.js and Assembler CSS 'x-' properties, we are going to use
'st-' properties as a way to extend divs metadata regarding its contents."
"PENDING: this is repeated in several snippets. Can be abstracted up in a common object of the class hierarchy?"
| output |
output := WriteStream on: ''.
output
nextPutAll: '<div st-class="' , self class greaseString , '"';
nextPut: Character lf;
nextPutAll: ' st-data="' , (STON toStringPretty: self metadata) , '">';
nextPut: Character lf.
^ output contents withInternetLineEndings.
]
{ #category : #'*MiniDocs' }
LeTextualSnippet >> metadataUpdate [
| createEmailSanitized editEmailSanitized |
@ -119,14 +108,46 @@ LeTextualSnippet >> metadataUpdate [
editEmailSanitized := self editEmail asString withoutXMLTagDelimiters.
^ OrderedDictionary new
at: 'id' put: self uidString;
at: 'parent' put: self parent uidString;
at: 'parent' put: (self parent ifNotNil: [self parent uidString ]);
at: 'created' put: self createTime asString;
at: 'modified' put: self latestEditTime asString;
at: 'creator' put: createEmailSanitized;
at: 'modifier' put: editEmailSanitized;
at: 'extra' put: self extra;
yourself
]
{ #category : #'*MiniDocs' }
LeTextualSnippet >> processSnippetAnnotations [
| exported substitutions annotations pageConfig |
annotations := self ast parts
select: [ :each | each className includesSubstring: 'AnnotationNode' ].
annotations ifEmpty: [ ^ self contentAsString ].
substitutions := OrderedDictionary new.
pageConfig := self page config.
annotations
do: [ :each |
| key type value color |
key := each source.
type := (key splitOn: ':') first copyWithoutAll: '{{'.
value := key copyFrom: type size + 4 to: key size - 2.
pageConfig
ifNil: [ color := 'default' ]
ifNotNil: [ | colors |
colors := pageConfig at: 'annotationColors' ifAbsent: [ nil ].
colors
ifNotNil: [ color := colors
at: type
ifAbsent: [ colors at: 'defaultColor' ifAbsentPut: [ 'default' ] ] ] ].
substitutions
at: key
put: '<span st-class="' , type , '" style="color:' , color , '">' , value , '</span>' ].
exported := self contentAsString.
substitutions
keysAndValuesDo: [ :k :v | exported := exported copyReplaceAll: k with: v ].
^ exported
]
{ #category : #'*MiniDocs' }
LeTextualSnippet >> sanitizeMetadata [
self options ifNil: [^ self ].
@ -141,5 +162,5 @@ LeTextualSnippet >> sanitizeMetadata [
{ #category : #'*MiniDocs' }
LeTextualSnippet >> tags [
^ self metadata at: 'tags' ifAbsentPut: [ Set new ]
^ self extra at: 'tags' ifAbsentPut: [ Set new ]
]

View File

@ -4,7 +4,7 @@ Class {
#instVars : [
'folder'
],
#category : #MiniDocs
#category : #'MiniDocs-Model'
}
{ #category : #accessing }
@ -29,5 +29,5 @@ Logseq >> journals [
{ #category : #accessing }
Logseq >> pages [
self folder/ 'pages'
^self folder/ 'pages'
]

View File

@ -27,6 +27,15 @@ Markdown >> asMarkdeep [
commentYAMLMetadata
]
{ #category : #accessing }
Markdown >> asMarkdownTiddler [
^ Tiddler new
title: self title;
text: self contents;
type: 'text/x-markdown';
created: Tiddler nowLocal.
]
{ #category : #accessing }
Markdown >> body [
^ body
@ -40,7 +49,7 @@ Markdown >> body: aString [
{ #category : #operation }
Markdown >> commentYAMLMetadata [
| newContents |
self detectYAMLMetadata ifFalse: [ ^ self ].
self contents detectYAMLMetadata ifFalse: [ ^ self ].
newContents := '' writeStream.
newContents nextPutAll: '<!--@yaml'; lf.
newContents nextPutAll: self yamlMetadataString.
@ -57,14 +66,15 @@ Markdown >> containsYAMLMetadataClosing [
{ #category : #accessing }
Markdown >> contents [
| response |
| response metadataString |
response := WriteStream on: ''.
metadataString := self metadataAsYAML
ifEmpty: [ '' ]
ifNotEmpty: [ '---', String cr, self metadataAsYAML, String cr, '---', String cr ].
response
nextPutAll: '---'; cr;
nextPutAll: self metadataAsYAML; cr;
nextPutAll: '---'; cr;
nextPutAll: self body.
^ response contents
nextPutAll: metadataString;
nextPutAll: (self body ifNil: [ '' ]).
^ response contents withInternetLineEndings
]
{ #category : #accessing }
@ -132,7 +142,7 @@ Markdown >> exportMetadataAsYaml [
{ #category : #accessing }
Markdown >> file [
^ file ifNil: [ file := FileLocator temp / 'temporalMarkdeep.md.html' ]
^ file ifNil: [ file := FileLocator temp / 'temporalMarkdeep.md' ]
]
{ #category : #accessing }
@ -149,8 +159,18 @@ Markdown >> fromFile: aFileReference [
{ #category : #'instance creation' }
Markdown >> fromString: markdownString [
(self metadata) at: 'original' put: markdownString yamlMetadata.
self body: markdownString contentsWithoutYAMLMetadata
| yamlMetadataRaw bodyTemp |
yamlMetadataRaw := (YamlHeaderParser parse: markdownString).
bodyTemp := '' writeStream.
(yamlMetadataRaw removeKey: 'body') do: [:paragraph |
bodyTemp nextPutAll: paragraph; cr; cr
].
self body: bodyTemp contents withInternetLineEndings.
(yamlMetadataRaw sanitizeMultilineValuesWith: markdownString)
ifNotNil: [
self metadata
ifEmpty: [ self metadata: yamlMetadataRaw ]
ifNotEmpty: [ self metadata at: 'hedgeDoc' put: yamlMetadataRaw ]].
]
{ #category : #accessing }
@ -179,6 +199,7 @@ Markdown >> lines [
Markdown >> metadata [
^ metadata ifNil: [ metadata := Dictionary new].
]
@ -219,3 +240,8 @@ Markdown >> printOn: aStream [
Markdown >> title [
^ title ifNil: [ title:= self headerAsTitle ]
]
{ #category : #accessing }
Markdown >> title: aString [
title := aString
]

View File

@ -34,11 +34,8 @@ MiniDocs class >> altShiftRightCombo [
{ #category : #accessing }
MiniDocs class >> appFolder [
| tempFolder userDataFolder |
userDataFolder := Smalltalk os isWindows
ifTrue: [ FileLocator home / 'AppData' / 'Local' ]
ifFalse: [ FileLocator userData ].
tempFolder := userDataFolder / 'Mutabit' / 'MiniDocs'.
| tempFolder |
tempFolder := ExoRepo userDataFolder / 'Mutabit' / 'MiniDocs'.
tempFolder exists ifFalse: [ tempFolder ensureCreateDirectory ].
^ tempFolder
]

View File

@ -1,64 +0,0 @@
"
I'm run an implementation of the [Nano ID](https://github.com/ai/nanoid) tiny, secure URL-friendly unique string ID generator via its [Nim implementation](https://github.com/icyphox/nanoid.nim).
The Nim script has hard coded:
* a [base 58 encoding](https://medium.com/concerning-pharo/understanding-base58-encoding-23e673e37ff6) alphabet to avoid similar looking letter and the use of non-alphanumeric characters.
* a 12 characters length output, which gives [a pretty low probability collision](https://zelark.github.io/nano-id-cc/) for the previous alphabet:
~616 years needed, in order to have a 1% probability of at least one collision at a speed of 1000 IDs per hour.
This is more than enough for our unique IDs applications, mostly in the documentation context,
which consists of hand crafted and/or programmatically produced notes ,
for example in data narratives, book(lets) and TiddlyWiki tiddlers of tens or hundreds of notes at most,
unevenly produced between hours, days and/or weeks..
The `External` tag is related on its dependency on other programming languages and frameworks,
though the dependency should be loaded by just loading a small binary with no dependencies.
"
Class {
#name : #NanoID,
#superclass : #Object,
#category : #'MiniDocs-External'
}
{ #category : #accessing }
NanoID class >> binaryFile [
^ MiniDocs appFolder / self scriptSourceCode basenameWithoutExtension
]
{ #category : #accessing }
NanoID class >> generate [
self binaryFile exists ifFalse: [ NanoID install].
Smalltalk os isWindows
ifTrue: [ ^ (LibC resultOfCommand:self binaryFile fullName) copyWithoutAll: (Character lf asString) ].
OSSUnixSubprocess new
command: self binaryFile fullName;
redirectStdout;
redirectStdout;
runAndWaitOnExitDo: [ :process :outString | ^ outString copyWithoutAll: (Character lf asString) ]
]
{ #category : #accessing }
NanoID class >> install [
"For the moment, only Gnu/Linux and Mac are supported.
IMPORTANT: Nimble, Nim's package manager should be installed, as this process doesn't verify its proper installation."
self binaryFile exists ifTrue: [ ^ MiniDocs appFolder ].
Nimble install: 'nanoid'.
Smalltalk os isWindows
ifTrue: [ ^ LibC resultOfCommand: 'nanoid c ',self scriptSourceCode fullName ].
OSSUnixSubprocess new
command: 'nim';
arguments: {'c'. self scriptSourceCode fullName};
runAndWaitOnExitDo: [ :process :outString |
(self scriptSourceCode parent / (self scriptSourceCode) basenameWithoutExtension) moveToPageTitled: MiniDocs appFolder asFileReference.
^ MiniDocs appFolder ]
]
{ #category : #accessing }
NanoID class >> isInstalled [
^ self binaryFile exists
]
{ #category : #accessing }
NanoID class >> scriptSourceCode [
^ FileLocator image parent / 'pharo-local/iceberg/Offray/MiniDocs/src/nanoIdGen.nim'
]

View File

@ -1,89 +0,0 @@
"
I'm a helper class modelling the common uses of the Nim's [Nimble package manager](https://github.com/nim-lang/nimble).
This was evolved in the context of the [Grafoscopio](mutabit.com/grafoscopio/en.html) community exploration and prototyping of interactive documentation.
"
Class {
#name : #Nimble,
#superclass : #Object,
#category : #'MiniDocs-External'
}
{ #category : #accessing }
Nimble class >> detect: packageName [
^ self installed
detect: [ :dependency | dependency beginsWith: packageName ]
ifFound: [ ^ true ]
ifNone: [ ^ false ]
]
{ #category : #accessing }
Nimble class >> install: packageName [
(self detect: packageName) ifTrue: [ ^ self ].
self installPackagesList.
Smalltalk os isWindows
ifTrue: [ ^ LibC runCommand: 'nimble install ', packageName ].
OSSUnixSubprocess new
command: 'nimble';
arguments: {'install'.
packageName};
redirectStdout;
redirectStderr;
runAndWaitOnExitDo: [ :process :outString :errString |
process isSuccess
ifTrue: [ Transcript show: 'Command exited correctly with output: ', outString. ]
ifFalse: [
^ 'Command exit with error status: ', process exitStatusInterpreter printString, String cr,
'Stderr contents: ', errString.
]
]
]
{ #category : #accessing }
Nimble class >> installPackagesList [
(FileLocator home / '.nimble' / 'packages_official.json') exists
ifTrue: [ ^ self ].
(Smalltalk os isUnix or: [ Smalltalk os isMacOS ])
ifTrue: [
OSSUnixSubprocess new
command: 'nimble';
arguments: #('refresh');
redirectStdout;
runAndWaitOnExitDo: [ :process :outString | ^ outString ].
].
Smalltalk os isWindows
ifTrue: [ ^ LibC resultOfCommand: 'nimble refresh' ]
]
{ #category : #accessing }
Nimble class >> installed [
Smalltalk os isWindows
ifTrue: [ | process |
process := GtExternalProcessBuilder new
command: 'nimble.exe';
args: #('list' '--installed');
output.
^ process stdout lines ].
OSSUnixSubprocess new
command: 'nimble';
arguments: #('list' '--installed');
redirectStdout;
redirectStderr;
runAndWaitOnExitDo: [ :process :outString :errString |
process isSuccess
ifTrue: [ ^ outString lines ];
ifFalse: [ ^ nil ]
]
]
{ #category : #accessing }
Nimble class >> version [
OSSUnixSubprocess new
command: 'nimble';
arguments: #('--version');
redirectStdout;
runAndWaitOnExitDo: [ :process :outString | ^ outString ]
]

View File

@ -10,19 +10,18 @@ OrderedDictionary >> asLepiterSnippet [
| response |
self at: 'className' ifAbsent: [ ^ nil ].
response := (self at: 'className') asClass new.
response fromString: (self at: 'content').
response
uid: (LeUID new uidString: (self at: 'id'));
parent: (self at: 'parent');
createTime: (LeTime new time: ((self at: 'created')asDateAndTime));
editTime: (LeTime new time: ((self at: 'modified') asDateAndTime));
editEmail: (self at: 'modifier');
createEmail: (self at: 'creator').
[ response fromDictionary: self ] onErrorDo: [ ].
[ response fromString: (self at: 'content') ] onErrorDo: [ ].
self at: 'origin' ifPresent: [ response metadata at: 'origin' put: (self at: 'origin') ].
self at: 'errata' ifPresent: [ response metadata at: 'errata' put: (self at: 'errata') ].
^ response
]
{ #category : #'*MiniDocs' }
OrderedDictionary >> asYAML [
^ (YQ jsonToYaml: self) accentedCharactersCorrection.
]
{ #category : #'*MiniDocs' }
OrderedDictionary >> errata [
^ self at: 'errata' ifAbsentPut: [ OrderedCollection new]
@ -33,3 +32,69 @@ OrderedDictionary >> redefineTimestampsBefore: dateAndTime [
self at: 'modified' put: dateAndTime asDateAndTime.
self at: 'created' put: dateAndTime asDateAndTime - 1 second.
]
{ #category : #'*MiniDocs' }
OrderedDictionary >> replaceNilsWith: aCharacter [
self associationsDo: [:each |
each value ifNil: [self at: each key put: aCharacter].
each value isDictionary ifTrue: [each value replaceNilsWith: aCharacter].
each value isArray ifTrue: [ | newArray|
newArray := (each value asDataSeries replaceNilsWith: aCharacter) asArray.
self at: each key put: newArray
]
]
]
{ #category : #'*MiniDocs' }
OrderedDictionary >> replaceWithUniqueNilsAndBooleansStartingAt: anInteger [
| totalNils shortUID |
totalNils := self flattened asDataSeries countNils.
shortUID := [NanoID generate copyFrom: 1 to: 3].
self associations doWithIndex: [:assoc :i | | subIndex |
subIndex := anInteger asString, '-', i asString.
assoc value
ifNil: [ self at: assoc key put: 'nil-', subIndex ].
assoc value isBoolean
ifTrue: [ self at: assoc key put: assoc value asString, '-', subIndex ].
assoc value isDictionary ifTrue: [assoc replaceWithUniqueNilsAndBooleansStartingAt: i].
assoc value isArray
ifTrue: [ self at: assoc key put: (assoc value replaceWithUniqueNilsAndBooleans)]
]
]
{ #category : #'*MiniDocs' }
OrderedDictionary >> sanitizeMultilineValuesWith: aString [
| toSanitize response |
toSanitize := OrderedCollection new.
response := OrderedCollection new.
self keysAndValuesDo: [:k :v |
(v isString and: [v lines size > 1])
ifTrue: [
aString lines
detect: [:line | line includesSubstring: k ]
ifFound: [:line | | sanitized|
sanitized := (line withoutPrefix: k, ':'), String cr,
v indentedWithExtraSpaces: 4.
self at: k put: sanitized ]
]
].
]
{ #category : #'*MiniDocs' }
OrderedDictionary >> treeView [
| view |
view := GtMondrian new.
view nodes
stencil: [ :x |
BlElement new
border: (BlBorder paint: Color black);
geometry: BlEllipseGeometry new;
layout: (BlLinearLayout new alignCenter);
addChild: (BlTextElement text: (x asRopedText fontSize: 10)) ];
with: (self flatCollectAsSet: #yourself) , self keys.
view edges
stencil: [ :x | BlLineElement new border: (BlBorder paint: (Color blue alpha: 0.5) width: 4) ];
connect: self associations from: #key toAll: #value.
view layout tree.
^ view
]

View File

@ -147,17 +147,11 @@ Pandoc class >> markdownToHtmlOnUnix: inputFile [
outputFile := inputFile parent / (inputFile basenameWithoutExtension , '.html').
outputFile ensureDelete.
outputFile ensureCreateFile.
OSSUnixSubprocess new
command: 'pandoc';
arguments: {'-f'. 'markdown+startnum+task_lists'. '--standalone'. '-t'. 'html'. inputFile fullName.
'--output'. outputFile fullName };
redirectStdout;
redirectStderr;
runAndWaitOnExitDo: [ :process :outString :errString |
process isSuccess
ifTrue: [ ^ outputFile ]
ifFalse: [ ^ inputFile ]
]
GtSubprocessWithInMemoryOutput new
shellCommand: 'pandoc -f markdown+startnum+task_lists --standalone -t html ', inputFile fullName, ' --output ', outputFile fullName;
runAndWait;
stdout.
^ outputFile.
]
{ #category : #converters }

View File

@ -11,7 +11,7 @@ Class {
'footnoteLabel',
'footnoteContent'
],
#category : #MiniDocs
#category : #'MiniDocs-Model'
}
{ #category : #accessing }

View File

@ -4,7 +4,7 @@ Extension { #name : #String }
String >> accentedCharactersCorrection [
| modified corrections |
corrections := {
'ó' -> 'ó' . º' -> 'ú' . 'ñ' -> 'ñ' .
'ó' -> 'ó' . “' -> 'Ó' . º' -> 'ú' . 'ñ' -> 'ñ' . 'Ñ' -> 'Ñ' .
'í' -> 'í' . 'á' -> 'á' . 'é' -> 'é' . '’' -> $' asString} asDictionary.
modified := self copy.
corrections keysAndValuesDo: [ :k :v |
@ -13,6 +13,41 @@ String >> accentedCharactersCorrection [
^ modified
]
{ #category : #'*MiniDocs' }
String >> admonitionBorderLines [
| response |
response := OrderedDictionary new.
self lines doWithIndex: [:line :index |
(self admonitionBorders includes: line trimBoth)
ifTrue: [ response at: index put: line trimBoth ]
].
^ response
]
{ #category : #'*MiniDocs' }
String >> admonitionBorders [
"For the moment I only work with the admonition starting border
as adding the closing one would imply to redo the #markdownSplitted
method implementing a proper parser, which, ATM is overkill."
| response |
response := #('info' 'success' 'warning' 'danger') collect: [ :each | ':::', each ].
^ response "copyWith: ':::'"
]
{ #category : #'*MiniDocs' }
String >> admonitionEndingPosition [
| response |
response := 0.
self startsWithMarkdownAdmonition ifFalse: [ ^ response ].
self lines do: [:line |
response > 0 ifTrue: [ response := response + 1 ].
(line trimBoth = ':::')
ifFalse: [ response := response + line size ]
ifTrue: [ ^ response := response + line size. ]
].
^ response
]
{ #category : #'*MiniDocs' }
String >> asDashedLowercase [
"I convert phrases like 'This is a phrase' into 'this-is-a-phrase'."
@ -70,6 +105,16 @@ String >> detectYAMLMetadata [
ifFound: [ ^ true ] ifNone: [ ^ false ] ]
]
{ #category : #'*MiniDocs' }
String >> indentedWithExtraSpaces: spaceNumber [
| response indent |
response := '' writeStream.
indent := String new.
spaceNumber timesRepeat: [ indent := indent, ' ' ].
self lines do: [:line | response nextPutAll: indent, line, String lf ].
^ response contents
]
{ #category : #'*MiniDocs' }
String >> markdownHeaders [
| response headers |
@ -82,6 +127,38 @@ String >> markdownHeaders [
^ response
]
{ #category : #'*MiniDocs' }
String >> markdownSplitLines [
"I'm useful for conversions between the HedgeDoc Markdown variant and Lepiter page snippets.
I provide broad places to where semantic breaks should be located in a page,
depending on headers or admonitions to create pages snippets with similar divisions.
Further page splits should be provided manually by the document author."
| response |
response := OrderedDictionary new.
response := response
addAll: self markdownHeaders;
addAll: self admonitionBorderLines;
yourself.
^ (response associations sorted: [ :x :y | x key < y key ]) asOrderedDictionary
]
{ #category : #'*MiniDocs' }
String >> markdownSplitted [
| response lastPart |
self markdownSplitLines ifEmpty: [ ^ self ].
response := OrderedCollection new.
self markdownSplitLines keys allButLast doWithIndex: [:key :index | | nextLine part |
nextLine := (self markdownSplitLines keys at: index + 1) - 1.
part := self lines copyFrom: key to: nextLine.
response add: part.
].
lastPart := self lines
copyFrom: self markdownSplitLines keys last
to: self lines size.
response add: lastPart.
^ response
]
{ #category : #'*MiniDocs' }
String >> promoteMarkdownHeaders [
| response |
@ -104,6 +181,12 @@ String >> romanizeAccents [
^ modified
]
{ #category : #'*MiniDocs' }
String >> startsWithMarkdownAdmonition [
self lines ifEmpty: [ ^ false ].
^ self admonitionBorders includes: self lines first trimBoth
]
{ #category : #'*MiniDocs' }
String >> startsWithYAMLMetadataDelimiter [
self lines ifEmpty: [^false].
@ -116,11 +199,6 @@ String >> withoutXMLTagDelimiters [
^ self copyWithoutAll: #($< $>)
]
{ #category : #'*MiniDocs' }
String >> yamlMetadata [
^ (YAML2JSON fromString: self yamlMetadataString)
]
{ #category : #'*MiniDocs' }
String >> yamlMetadataClosingLineNumber [
"I return the line where the closing of the YAML metadata occurs or 0 if no closing is found."

View File

@ -0,0 +1,9 @@
Extension { #name : #XMLDocument }
{ #category : #'*MiniDocs' }
XMLDocument >> detectMarkdeepTitle [
| titleLine |
titleLine := (self nodesCollect: [:node | node contentString ]) first lines
detect: [:line | line includesSubstring: ' **'] ifNone: ['Untitled'].
^ titleLine trimmed trimBoth: [:char | char = $* ]
]

View File

@ -1,52 +0,0 @@
"
The `External` tag is related on its dependency on other programming languages and frameworks,
though the dependency should be loaded by just loading a small binary with no dependencies.
"
Class {
#name : #YQ,
#superclass : #Object,
#category : #'MiniDocs-External'
}
{ #category : #accessing }
YQ class >> binaryDownloadLinkFor: operativeSystem on: processor [
| binaryName binaryDownloadData |
binaryName := 'yq_', operativeSystem , '_', processor.
binaryDownloadData := ((self lastReleaseData at: 'assets')
select: [:each | (each at: 'name') beginsWith: binaryName ]) first.
^ binaryDownloadData at: 'browser_download_url'
]
{ #category : #accessing }
YQ class >> binaryFile [
"Starting with location on Arch Linux and its derivates. Multidistro and multiOS support should be added."
^ FileLocator root / 'usr/bin/yq'
]
{ #category : #accessing }
YQ class >> install [
^ self lastReleaseData
]
{ #category : #accessing }
YQ class >> jsonToYaml: aDictionary [
| jsonFile |
self binaryFile exists ifFalse: [ YQ install].
jsonFile := MarkupFile exportAsFileOn: FileLocator temp / 'data.json' containing: aDictionary.
(Smalltalk os isUnix or: [ Smalltalk os isMacOS ])
ifTrue: [
OSSUnixSubprocess new
shellCommand: 'cat ', jsonFile fullName,' | yq -y';
redirectStdout;
runAndWaitOnExitDo: [ :command :outString |
^ outString
]].
Smalltalk os isWindows
ifTrue: [ ^ LibC resultOfCommand: 'yq -p=json ', jsonFile fullName ].
]
{ #category : #accessing }
YQ class >> lastReleaseData [
^ (STONJSON
fromString: 'https://api.github.com/repos/mikefarah/yq/releases' asUrl retrieveContents) first
]

View File

@ -0,0 +1,10 @@
Extension { #name : #ZnConstants }
{ #category : #'*MiniDocs' }
ZnConstants class >> maximumLineLength [
"Return the maximum line length to accept.
Used by ZnLineReader and thus for reading request/status lines as well as headers.
This helps to protect us from malicious content."
^ 5096 "8192"
]