diff --git a/.gitignore b/.gitignore index 723fd9c..755edbd 100644 --- a/.gitignore +++ b/.gitignore @@ -5,6 +5,8 @@ *.exe +docs/*.html +!docs/.gitkeep # file prefixed by x_ are ignored x_* diff --git a/dates.md b/dates.md index a46807f..d36e912 100644 --- a/dates.md +++ b/dates.md @@ -6,7 +6,7 @@ noting here for convenience publication dates (taken from git commits, will add - Mar 18: display_multidim_arrays - Mar 24: pixie_chessboard - Mar 24: random_palette -- Apr 13: bernulli_and_beyond +- Apr 13: bernoulli_and_beyond - May 7: mustache_specs - May 15: toc_mustache diff --git a/docs/.gitkeep b/docs/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/docs/arraymancer_tutorial.json b/docs/arraymancer_tutorial.json new file mode 100644 index 0000000..f034d33 --- /dev/null +++ b/docs/arraymancer_tutorial.json @@ -0,0 +1,4 @@ +{ + "data": {"pub_date":{"day":27,"year":2023,"month":3},"path_to_root":".","github_remote_url":"https://github.com/pietroppeter/nblog","path_to_here":"arraymancer_tutorial.nim","highlight":"","github_logo":"","source":"import nimib\nimport nblog\n\nnbInit(theme = useNblog)\nnb.title \"Arraymancer Tutorial - First steps\"\nnb.subtitle \"A remake of the original tutorial using nimib\"\nnb.pubDate 2023, 3, 27\n\nnbText: \"\"\"\n> original tutorial: \n>\n> I will note differences with the original in quoted sections.\n\n## Tensor properties\n\nTensors have the following properties:\n- `rank`: 0 for scalar (cannot be stored), 1 for vector, 2 for matrices, *N* for *N* dimensional arrays\n- `shape`: a sequence of the tensor dimensions along each axis\n\nNext properties are technical and there for completeness:\n- `stride`: a sequence of numbers of steps to get the next item along a dimension\n- `offset`: the first element of the tensor\n\"\"\"\n# order of variable names (d, c, e, ..., a, b) I guess it reflects the original order of the sections.\nnbCode:\n import arraymancer, sugar, sequtils\n\n let d = [[1, 2, 3], [4, 5, 6]].toTensor()\n\n echo d\n\nnbText: \"\"\"> message changed, it was: `Tensor of shape 2x3 of type \"int\" on backend \"Cpu\"`\"\"\"\n\nnbCode:\n dump d.rank\n dump d.shape\n dump d.strides ## [x,y] => next row is x elements away in memory while next column is 1 element away.\n dump d.offset\nnbText: \"> echo of shape and strides changed (dropped @)\"\n\nnbText: \"\"\"\n## Tensor creation\nThe canonical way to initialize a tensor is by converting a seq of seq of ... or an array of array of ...\ninto a tensor using `toTensor`.\n`toTensor` supports deep nested sequences and arrays, even sequences of array of sequences.\n\"\"\"\n\nnbCode:\n let c = [\n [\n [1,2,3],\n [4,5,6]\n ],\n [\n [11,22,33],\n [44,55,66]\n ],\n [\n [111,222,333],\n [444,555,666]\n ],\n [\n [1111,2222,3333],\n [4444,5555,6666]\n ]\n ].toTensor()\n echo c\nnbText: \"> I am not sure where the additional pipes come from, maybe a bug?\"\nnbText: \"\"\"\n`newTensor` procedure can be used to initialize a tensor of a specific\nshape with a default value. (0 for numbers, false for bool...)\n\n`zeros` and `ones` procedures create a new tensor filled with 0 and\n1 respectively.\n\n`zeros_like` and `ones_like` take an input tensor and output a\ntensor of the same shape but filled with 0 and 1 respectively.\n\"\"\"\nnbCode:\n let e = newTensor[bool]([2, 3])\n dump e\nnbCode:\n let f = zeros[float]([4, 3])\n dump f\nnbCode:\n let g = ones[float]([4, 3])\n dump g\nnbCode:\n let tmp = [[1,2],[3,4]].toTensor()\n let h = tmp.zeros_like\n dump h\nnbCode:\n let i = tmp.ones_like\n dump i\n\nnbText: \"\"\"\n## Accessing and modifying a value\n\nTensors value can be retrieved or set with array brackets.\n\"\"\"\n# need to import sequtils to have toSeq\nnbCode:\n var a = toSeq(1..24).toTensor().reshape(2,3,4)\n echo a\nnbCode:\n dump a[1, 1, 1]\n echo a\nnbCode:\n a[1, 1, 1] = 999\n echo a\nnbText: \"\"\"\n## Copying\n\nWarning ⚠: When you do the following, both tensors `a` and `b` will share data.\nFull copy must be explicitly requested via the `clone` function.\n\"\"\"\nblock: # using a block I can reuse a\n nbCode:\n let a = toSeq(1..24).toTensor().reshape(2,3,4)\n var b = a\n var c = clone(a)\n nbText: \"\"\"\n Here modifying `b` WILL modify `a`.\n\n > adding an example of modification and an example of clone:\n \"\"\"\n # still in block scope in order to reuse b\n nbCode:\n dump a[1, 0, 0]\n c[1, 0, 0] = 0\n dump a[1, 0, 0]\n b[1, 0, 0] = 0\n dump a[1, 0, 0]\nnbText: \"\"\"\nThis behaviour is the same as Numpy and Julia,\nreasons can be found in the following\n[under the hood article](https://mratsim.github.io/Arraymancer/uth.copy_semantics.html).\n\"\"\"\n\nnbSaveJson\n","favicon":"🐳\">","stylesheet":"","nb_style":"","title":"Arraymancer Tutorial - First steps - A remake of the original tutorial using nimib","source_highlighted":"import nimib\nimport nblog\n\nnbInit(theme = useNblog)\nnb.title "Arraymancer Tutorial - First steps"\nnb.subtitle "A remake of the original tutorial using nimib"\nnb.pubDate 2023, 3, 27\n\nnbText: """\n> original tutorial: <https://mratsim.github.io/Arraymancer/tuto.first_steps.html>\n>\n> I will note differences with the original in quoted sections.\n\n## Tensor properties\n\nTensors have the following properties:\n- `rank`: 0 for scalar (cannot be stored), 1 for vector, 2 for matrices, *N* for *N* dimensional arrays\n- `shape`: a sequence of the tensor dimensions along each axis\n\nNext properties are technical and there for completeness:\n- `stride`: a sequence of numbers of steps to get the next item along a dimension\n- `offset`: the first element of the tensor\n"""\n# order of variable names (d, c, e, ..., a, b) I guess it reflects the original order of the sections.\nnbCode:\n import arraymancer, sugar, sequtils\n\n let d = [[1, 2, 3], [4, 5, 6]].toTensor()\n\n echo d\n\nnbText: """> message changed, it was: `Tensor of shape 2x3 of type "int" on backend "Cpu"`"""\n\nnbCode:\n dump d.rank\n dump d.shape\n dump d.strides ## [x,y] => next row is x elements away in memory while next column is 1 element away.\n dump d.offset\nnbText: "> echo of shape and strides changed (dropped @)"\n\nnbText: """\n## Tensor creation\nThe canonical way to initialize a tensor is by converting a seq of seq of ... or an array of array of ...\ninto a tensor using `toTensor`.\n`toTensor` supports deep nested sequences and arrays, even sequences of array of sequences.\n"""\n\nnbCode:\n let c = [\n [\n [1,2,3],\n [4,5,6]\n ],\n [\n [11,22,33],\n [44,55,66]\n ],\n [\n [111,222,333],\n [444,555,666]\n ],\n [\n [1111,2222,3333],\n [4444,5555,6666]\n ]\n ].toTensor()\n echo c\nnbText: "> I am not sure where the additional pipes come from, maybe a bug?"\nnbText: """\n`newTensor` procedure can be used to initialize a tensor of a specific\nshape with a default value. (0 for numbers, false for bool...)\n\n`zeros` and `ones` procedures create a new tensor filled with 0 and\n1 respectively.\n\n`zeros_like` and `ones_like` take an input tensor and output a\ntensor of the same shape but filled with 0 and 1 respectively.\n"""\nnbCode:\n let e = newTensor[bool]([2, 3])\n dump e\nnbCode:\n let f = zeros[float]([4, 3])\n dump f\nnbCode:\n let g = ones[float]([4, 3])\n dump g\nnbCode:\n let tmp = [[1,2],[3,4]].toTensor()\n let h = tmp.zeros_like\n dump h\nnbCode:\n let i = tmp.ones_like\n dump i\n\nnbText: """\n## Accessing and modifying a value\n\nTensors value can be retrieved or set with array brackets.\n"""\n# need to import sequtils to have toSeq\nnbCode:\n var a = toSeq(1..24).toTensor().reshape(2,3,4)\n echo a\nnbCode:\n dump a[1, 1, 1]\n echo a\nnbCode:\n a[1, 1, 1] = 999\n echo a\nnbText: """\n## Copying\n\nWarning ⚠: When you do the following, both tensors `a` and `b` will share data.\nFull copy must be explicitly requested via the `clone` function.\n"""\nblock: # using a block I can reuse a\n nbCode:\n let a = toSeq(1..24).toTensor().reshape(2,3,4)\n var b = a\n var c = clone(a)\n nbText: """\n Here modifying `b` WILL modify `a`.\n\n > adding an example of modification and an example of clone:\n """\n # still in block scope in order to reuse b\n nbCode:\n dump a[1, 0, 0]\n c[1, 0, 0] = 0\n dump a[1, 0, 0]\n b[1, 0, 0] = 0\n dump a[1, 0, 0]\nnbText: """\nThis behaviour is the same as Numpy and Julia,\nreasons can be found in the following\n[under the hood article](https://mratsim.github.io/Arraymancer/uth.copy_semantics.html).\n"""\n\nnbSaveJson\n"}, + "blocks": [{"command":"nbText","code":"","output":"# Arraymancer Tutorial - First steps","context":{"code":"","output":"# Arraymancer Tutorial - First steps"}},{"command":"nbText","code":"","output":"## A remake of the original tutorial using nimib","context":{"code":"","output":"## A remake of the original tutorial using nimib"}},{"command":"nbRawHtml","code":"","output":"

","context":{"code":"","output":"

"}},{"command":"nbText","code":"","output":"> original tutorial: \n>\n> I will note differences with the original in quoted sections.\n\n## Tensor properties\n\nTensors have the following properties:\n- `rank`: 0 for scalar (cannot be stored), 1 for vector, 2 for matrices, *N* for *N* dimensional arrays\n- `shape`: a sequence of the tensor dimensions along each axis\n\nNext properties are technical and there for completeness:\n- `stride`: a sequence of numbers of steps to get the next item along a dimension\n- `offset`: the first element of the tensor\n","context":{"code":"","output":"> original tutorial: \n>\n> I will note differences with the original in quoted sections.\n\n## Tensor properties\n\nTensors have the following properties:\n- `rank`: 0 for scalar (cannot be stored), 1 for vector, 2 for matrices, *N* for *N* dimensional arrays\n- `shape`: a sequence of the tensor dimensions along each axis\n\nNext properties are technical and there for completeness:\n- `stride`: a sequence of numbers of steps to get the next item along a dimension\n- `offset`: the first element of the tensor"}},{"command":"nbCode","code":"import arraymancer, sugar, sequtils\n\nlet d = [[1, 2, 3], [4, 5, 6]].toTensor()\n\necho d","output":"Tensor[system.int] of shape \"[2, 3]\" on backend \"Cpu\"\n|1 2 3|\n|4 5 6|\n","context":{"code":"import arraymancer, sugar, sequtils\n\nlet d = [[1, 2, 3], [4, 5, 6]].toTensor()\n\necho d","output":"Tensor[system.int] of shape \"[2, 3]\" on backend \"Cpu\"\n|1 2 3|\n|4 5 6|"}},{"command":"nbText","code":"","output":"> message changed, it was: `Tensor of shape 2x3 of type \"int\" on backend \"Cpu\"`","context":{"code":"","output":"> message changed, it was: `Tensor of shape 2x3 of type \"int\" on backend \"Cpu\"`"}},{"command":"nbCode","code":"dump d.rank\ndump d.shape\ndump d.strides ## [x,y] => next row is x elements away in memory while next column is 1 element away.\ndump d.offset","output":"d.rank = 2\nd.shape = [2, 3]\nd.strides = [3, 1]\nd.offset = 0\n","context":{"code":"dump d.rank\ndump d.shape\ndump d.strides ## [x,y] => next row is x elements away in memory while next column is 1 element away.\ndump d.offset","output":"d.rank = 2\nd.shape = [2, 3]\nd.strides = [3, 1]\nd.offset = 0"}},{"command":"nbText","code":"","output":"> echo of shape and strides changed (dropped @)","context":{"code":"","output":"> echo of shape and strides changed (dropped @)"}},{"command":"nbText","code":"","output":"## Tensor creation\nThe canonical way to initialize a tensor is by converting a seq of seq of ... or an array of array of ...\ninto a tensor using `toTensor`.\n`toTensor` supports deep nested sequences and arrays, even sequences of array of sequences.\n","context":{"code":"","output":"## Tensor creation\nThe canonical way to initialize a tensor is by converting a seq of seq of ... or an array of array of ...\ninto a tensor using `toTensor`.\n`toTensor` supports deep nested sequences and arrays, even sequences of array of sequences."}},{"command":"nbCode","code":"let c = [\n [\n [1,2,3],\n [4,5,6]\n ],\n [\n [11,22,33],\n [44,55,66]\n ],\n [\n [111,222,333],\n [444,555,666]\n ],\n [\n [1111,2222,3333],\n [4444,5555,6666]\n ]\n ].toTensor()\necho c","output":"Tensor[system.int] of shape \"[4, 2, 3]\" on backend \"Cpu\"\n 0 1 2 3 \n|1 2 3| |11 22 33| |111 222 333| |1111 2222 3333|\n|4 5 6| |44 55 66| |444 555 666| |4444 5555 6666|\n\n","context":{"code":"let c = [\n [\n [1,2,3],\n [4,5,6]\n ],\n [\n [11,22,33],\n [44,55,66]\n ],\n [\n [111,222,333],\n [444,555,666]\n ],\n [\n [1111,2222,3333],\n [4444,5555,6666]\n ]\n ].toTensor()\necho c","output":"Tensor[system.int] of shape \"[4, 2, 3]\" on backend \"Cpu\"\n 0 1 2 3 \n|1 2 3| |11 22 33| |111 222 333| |1111 2222 3333|\n|4 5 6| |44 55 66| |444 555 666| |4444 5555 6666|"}},{"command":"nbText","code":"","output":"> I am not sure where the additional pipes come from, maybe a bug?","context":{"code":"","output":"> I am not sure where the additional pipes come from, maybe a bug?"}},{"command":"nbText","code":"","output":"`newTensor` procedure can be used to initialize a tensor of a specific\nshape with a default value. (0 for numbers, false for bool...)\n\n`zeros` and `ones` procedures create a new tensor filled with 0 and\n1 respectively.\n\n`zeros_like` and `ones_like` take an input tensor and output a\ntensor of the same shape but filled with 0 and 1 respectively.\n","context":{"code":"","output":"`newTensor` procedure can be used to initialize a tensor of a specific\nshape with a default value. (0 for numbers, false for bool...)\n\n`zeros` and `ones` procedures create a new tensor filled with 0 and\n1 respectively.\n\n`zeros_like` and `ones_like` take an input tensor and output a\ntensor of the same shape but filled with 0 and 1 respectively."}},{"command":"nbCode","code":"let e = newTensor[bool]([2, 3])\ndump e","output":"e = Tensor[system.bool] of shape \"[2, 3]\" on backend \"Cpu\"\n|false false false|\n|false false false|\n","context":{"code":"let e = newTensor[bool]([2, 3])\ndump e","output":"e = Tensor[system.bool] of shape \"[2, 3]\" on backend \"Cpu\"\n|false false false|\n|false false false|"}},{"command":"nbCode","code":"let f = zeros[float]([4, 3])\ndump f","output":"f = Tensor[system.float] of shape \"[4, 3]\" on backend \"Cpu\"\n|0 0 0|\n|0 0 0|\n|0 0 0|\n|0 0 0|\n","context":{"code":"let f = zeros[float]([4, 3])\ndump f","output":"f = Tensor[system.float] of shape \"[4, 3]\" on backend \"Cpu\"\n|0 0 0|\n|0 0 0|\n|0 0 0|\n|0 0 0|"}},{"command":"nbCode","code":"let g = ones[float]([4, 3])\ndump g","output":"g = Tensor[system.float] of shape \"[4, 3]\" on backend \"Cpu\"\n|1 1 1|\n|1 1 1|\n|1 1 1|\n|1 1 1|\n","context":{"code":"let g = ones[float]([4, 3])\ndump g","output":"g = Tensor[system.float] of shape \"[4, 3]\" on backend \"Cpu\"\n|1 1 1|\n|1 1 1|\n|1 1 1|\n|1 1 1|"}},{"command":"nbCode","code":"let tmp = [[1,2],[3,4]].toTensor()\nlet h = tmp.zeros_like\ndump h","output":"h = Tensor[system.int] of shape \"[2, 2]\" on backend \"Cpu\"\n|0 0|\n|0 0|\n","context":{"code":"let tmp = [[1,2],[3,4]].toTensor()\nlet h = tmp.zeros_like\ndump h","output":"h = Tensor[system.int] of shape \"[2, 2]\" on backend \"Cpu\"\n|0 0|\n|0 0|"}},{"command":"nbCode","code":"let i = tmp.ones_like\ndump i","output":"i = Tensor[system.int] of shape \"[2, 2]\" on backend \"Cpu\"\n|1 1|\n|1 1|\n","context":{"code":"let i = tmp.ones_like\ndump i","output":"i = Tensor[system.int] of shape \"[2, 2]\" on backend \"Cpu\"\n|1 1|\n|1 1|"}},{"command":"nbText","code":"","output":"## Accessing and modifying a value\n\nTensors value can be retrieved or set with array brackets.\n","context":{"code":"","output":"## Accessing and modifying a value\n\nTensors value can be retrieved or set with array brackets."}},{"command":"nbCode","code":"var a = toSeq(1..24).toTensor().reshape(2,3,4)\necho a","output":"Tensor[system.int] of shape \"[2, 3, 4]\" on backend \"Cpu\"\n 0 1 \n|1 2 3 4| |13 14 15 16|\n|5 6 7 8| |17 18 19 20|\n|9 10 11 12| |21 22 23 24|\n\n","context":{"code":"var a = toSeq(1..24).toTensor().reshape(2,3,4)\necho a","output":"Tensor[system.int] of shape \"[2, 3, 4]\" on backend \"Cpu\"\n 0 1 \n|1 2 3 4| |13 14 15 16|\n|5 6 7 8| |17 18 19 20|\n|9 10 11 12| |21 22 23 24|"}},{"command":"nbCode","code":"dump a[1, 1, 1]\necho a","output":"a[1, 1, 1] = 18\nTensor[system.int] of shape \"[2, 3, 4]\" on backend \"Cpu\"\n 0 1 \n|1 2 3 4| |13 14 15 16|\n|5 6 7 8| |17 18 19 20|\n|9 10 11 12| |21 22 23 24|\n\n","context":{"code":"dump a[1, 1, 1]\necho a","output":"a[1, 1, 1] = 18\nTensor[system.int] of shape \"[2, 3, 4]\" on backend \"Cpu\"\n 0 1 \n|1 2 3 4| |13 14 15 16|\n|5 6 7 8| |17 18 19 20|\n|9 10 11 12| |21 22 23 24|"}},{"command":"nbCode","code":"a[1, 1, 1] = 999\necho a","output":"Tensor[system.int] of shape \"[2, 3, 4]\" on backend \"Cpu\"\n 0 1 \n|1 2 3 4| |13 14 15 16|\n|5 6 7 8| |17 999 19 20|\n|9 10 11 12| |21 22 23 24|\n\n","context":{"code":"a[1, 1, 1] = 999\necho a","output":"Tensor[system.int] of shape \"[2, 3, 4]\" on backend \"Cpu\"\n 0 1 \n|1 2 3 4| |13 14 15 16|\n|5 6 7 8| |17 999 19 20|\n|9 10 11 12| |21 22 23 24|"}},{"command":"nbText","code":"","output":"## Copying\n\nWarning ⚠: When you do the following, both tensors `a` and `b` will share data.\nFull copy must be explicitly requested via the `clone` function.\n","context":{"code":"","output":"## Copying\n\nWarning ⚠: When you do the following, both tensors `a` and `b` will share data.\nFull copy must be explicitly requested via the `clone` function."}},{"command":"nbCode","code":"let a = toSeq(1..24).toTensor().reshape(2,3,4)\nvar b = a\nvar c = clone(a)","output":"","context":{"code":"let a = toSeq(1..24).toTensor().reshape(2,3,4)\nvar b = a\nvar c = clone(a)","output":""}},{"command":"nbText","code":"","output":" Here modifying `b` WILL modify `a`.\n\n > adding an example of modification and an example of clone:\n ","context":{"code":"","output":" Here modifying `b` WILL modify `a`.\n\n > adding an example of modification and an example of clone:\n "}},{"command":"nbCode","code":"dump a[1, 0, 0]\nc[1, 0, 0] = 0\ndump a[1, 0, 0]\nb[1, 0, 0] = 0\ndump a[1, 0, 0]","output":"a[1, 0, 0] = 13\na[1, 0, 0] = 13\na[1, 0, 0] = 0\n","context":{"code":"dump a[1, 0, 0]\nc[1, 0, 0] = 0\ndump a[1, 0, 0]\nb[1, 0, 0] = 0\ndump a[1, 0, 0]","output":"a[1, 0, 0] = 13\na[1, 0, 0] = 13\na[1, 0, 0] = 0"}},{"command":"nbText","code":"","output":"This behaviour is the same as Numpy and Julia,\nreasons can be found in the following\n[under the hood article](https://mratsim.github.io/Arraymancer/uth.copy_semantics.html).\n","context":{"code":"","output":"This behaviour is the same as Numpy and Julia,\nreasons can be found in the following\n[under the hood article](https://mratsim.github.io/Arraymancer/uth.copy_semantics.html)."}}] +} \ No newline at end of file diff --git a/docs/city_in_a_bottle.json b/docs/city_in_a_bottle.json new file mode 100644 index 0000000..286184d --- /dev/null +++ b/docs/city_in_a_bottle.json @@ -0,0 +1,4 @@ +{ + "data": {"pub_date":{"day":12,"year":2022,"month":7},"path_to_root":".","github_remote_url":"https://github.com/pietroppeter/nblog","path_to_here":"city_in_a_bottle.nim","highlight":"","github_logo":"","source":"import nimib\nimport nblog\n\nnbInit(theme = useNblog)\nnb.title \"City in a bottle\"\nnb.subtitle \"How to embed a tweet in a nimib document\"\nnb.pubDate 2022, 7, 12\n\nnbText: \"\"\"\nHow do I embed a tweet like [this great tweet](https://twitter.com/killedbyapixel/status/1517294627996545024) in nimib?\n\nEasy! Just go in the top right corner of the tweet (⋮), click on \"Embed Tweet\", copy the code\nand use `nbRawHtml`:\n\"\"\"\nnimibCode:\n nbRawHtml: \"\"\"

A City in a Bottle 🌆

<canvas style=width:99% id=c onclick=setInterval('for(c.width=w=99,++t,i=6e3;i--;c.getContext`2d`.fillRect(i%w,i/w|0,1-d*Z/w+s,1))for(a=i%w/50-1,s=b=1-i/4e3,X=t,Y=Z=d=1;++Z<w&(Y<6-(32<Z&27<X%w&&X/9^Z/8)*8%46||d|(s=(X&Y&Z)%3/Z,a=b=1,d=Z/w));Y-=b)X+=a',t=9)> pic.twitter.com/N3WElPqtMY

— Frank Force 🌻 (@KilledByAPixel) April 22, 2022
\"\"\"\n\nnbText: \"And we can use `nbRawHtml` also to visualize the canvas code. The only adjustment I made is to add a solid border. Click inside to start the animation.\"\nnbRawHtml: \"\"\"\"\"\"\n\nnbText: \"\"\"Not sure why, but after you click a big white vertical area is created.\n\nAnyhoo, enjoy another tweet that links to a great Observable document that explains the code\n(I guess that now we should be able to reproduce that observable document in nimib...):\n\"\"\"\nnbRawHtml: \"\"\"

If you'd like to learn how the code works, I recommend checking out this amazing breakdown by @DanielDarabos that reorganizes and comments the code and provides controls to play with it.https://t.co/SVtC0yF0gJ

— Frank Force 🌻 (@KilledByAPixel) May 26, 2022
\"\"\"\n\nnbText: \"And actually this content was inspired by the following tweet exchange that brought back in my timeline the amazing city in a bottle:\"\nnbRawHtml: \"\"\"

It has its limits! I tried running @KilledByAPixel's amazing City in a Bottle tweet-length shader through it, and GPT-3 replied with the equivalent of a ¯\\_(ツ)_/¯ https://t.co/n1c4oNEwNC pic.twitter.com/FeYixu7JS6

— Andy Baio (@waxpancake) July 11, 2022
\"\"\"\n\nnbText: \"Do I like Twitter? Oh, boy...\"\nnbRawHtml: \"\"\"

Oh, boy! West Coast, #YoungSheldon starts now! pic.twitter.com/bpSn7NySME

— Young Sheldon (@YoungSheldon) January 4, 2019
\"\"\"\n\nnbSaveJson","favicon":"🐳\">","stylesheet":"","nb_style":"","title":"City in a bottle - How to embed a tweet in a nimib document","source_highlighted":"import nimib\nimport nblog\n\nnbInit(theme = useNblog)\nnb.title "City in a bottle"\nnb.subtitle "How to embed a tweet in a nimib document"\nnb.pubDate 2022, 7, 12\n\nnbText: """\nHow do I embed a tweet like [this great tweet](https://twitter.com/killedbyapixel/status/1517294627996545024) in nimib?\n\nEasy! Just go in the top right corner of the tweet (⋮), click on "Embed Tweet", copy the code\nand use `nbRawHtml`:\n"""\nnimibCode:\n nbRawHtml: """<blockquote class="twitter-tweet"><p lang="en" dir="ltr">A City in a Bottle 🌆<br><br>&lt;canvas style=width:99% id=c onclick=setInterval(&#39;for(c.width=w=99,++t,i=6e3;i--;c.getContext`2d`.fillRect(i%w,i/w|0,1-d*Z/w+s,1))for(a=i%w/50-1,s=b=1-i/4e3,X=t,Y=Z=d=1;++Z&lt;w&amp;(Y&lt;6-(32&lt;Z&amp;27&lt;X%w&amp;&amp;X/9^Z/8)*8%46||d|(s=(X&amp;Y&amp;Z)%3/Z,a=b=1,d=Z/w));Y-=b)X+=a&#39;,t=9)&gt; <a href="https://t.co/N3WElPqtMY">pic.twitter.com/N3WElPqtMY</a></p>&mdash; Frank Force 🌻 (@KilledByAPixel) <a href="https://twitter.com/KilledByAPixel/status/1517294627996545024?ref_src=twsrc%5Etfw">April 22, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>"""\n\nnbText: "And we can use `nbRawHtml` also to visualize the canvas code. The only adjustment I made is to add a solid border. Click inside to start the animation."\nnbRawHtml: """<canvas style="width:99%; border:1px solid #000000;" id=c onclick=setInterval('for(c.width=w=99,++t,i=6e3;i--;c.getContext`2d`.fillRect(i%w,i/w|0,1-d*Z/w+s,1))for(a=i%w/50-1,s=b=1-i/4e3,X=t,Y=Z=d=1;++Z<w&(Y<6-(32<Z&27<X%w&&X/9^Z/8)*8%46||d|(s=(X&Y&Z)%3/Z,a=b=1,d=Z/w));Y-=b)X+=a',t=9)></canvas>"""\n\nnbText: """Not sure why, but after you click a big white vertical area is created.\n\nAnyhoo, enjoy another tweet that links to a great Observable document that explains the code\n(I guess that now we should be able to reproduce that observable document in nimib...):\n"""\nnbRawHtml: """<blockquote class="twitter-tweet"><p lang="en" dir="ltr">If you&#39;d like to learn how the code works, I recommend checking out this amazing breakdown by <a href="https://twitter.com/DanielDarabos?ref_src=twsrc%5Etfw">@DanielDarabos</a> that reorganizes and comments the code and provides controls to play with it.<a href="https://t.co/SVtC0yF0gJ">https://t.co/SVtC0yF0gJ</a></p>&mdash; Frank Force 🌻 (@KilledByAPixel) <a href="https://twitter.com/KilledByAPixel/status/1529838360235220992?ref_src=twsrc%5Etfw">May 26, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>"""\n\nnbText: "And actually this content was inspired by the following tweet exchange that brought back in my timeline the amazing city in a bottle:"\nnbRawHtml: """<blockquote class="twitter-tweet"><p lang="en" dir="ltr">It has its limits! I tried running <a href="https://twitter.com/KilledByAPixel?ref_src=twsrc%5Etfw">@KilledByAPixel</a>&#39;s amazing City in a Bottle tweet-length shader through it, and GPT-3 replied with the equivalent of a ¯\\_(ツ)_/¯ <a href="https://t.co/n1c4oNEwNC">https://t.co/n1c4oNEwNC</a> <a href="https://t.co/FeYixu7JS6">pic.twitter.com/FeYixu7JS6</a></p>&mdash; Andy Baio (@waxpancake) <a href="https://twitter.com/waxpancake/status/1546634381183164416?ref_src=twsrc%5Etfw">July 11, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>"""\n\nnbText: "Do I like Twitter? Oh, boy..."\nnbRawHtml: """<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Oh, boy! West Coast, <a href="https://twitter.com/hashtag/YoungSheldon?src=hash&amp;ref_src=twsrc%5Etfw">#YoungSheldon</a> starts now! <a href="https://t.co/bpSn7NySME">pic.twitter.com/bpSn7NySME</a></p>&mdash; Young Sheldon (@YoungSheldon) <a href="https://twitter.com/YoungSheldon/status/1081045053798273024?ref_src=twsrc%5Etfw">January 4, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>"""\n\nnbSaveJson"}, + "blocks": [{"command":"nbText","code":"","output":"# City in a bottle","context":{"code":"","output":"# City in a bottle"}},{"command":"nbText","code":"","output":"## How to embed a tweet in a nimib document","context":{"code":"","output":"## How to embed a tweet in a nimib document"}},{"command":"nbRawHtml","code":"","output":"

","context":{"code":"","output":"

"}},{"command":"nbText","code":"","output":"How do I embed a tweet like [this great tweet](https://twitter.com/killedbyapixel/status/1517294627996545024) in nimib?\n\nEasy! Just go in the top right corner of the tweet (⋮), click on \"Embed Tweet\", copy the code\nand use `nbRawHtml`:\n","context":{"code":"","output":"How do I embed a tweet like [this great tweet](https://twitter.com/killedbyapixel/status/1517294627996545024) in nimib?\n\nEasy! Just go in the top right corner of the tweet (⋮), click on \"Embed Tweet\", copy the code\nand use `nbRawHtml`:"}},{"command":"nimibCode","code":"nbRawHtml: \"\"\"

A City in a Bottle 🌆

<canvas style=width:99% id=c onclick=setInterval('for(c.width=w=99,++t,i=6e3;i--;c.getContext`2d`.fillRect(i%w,i/w|0,1-d*Z/w+s,1))for(a=i%w/50-1,s=b=1-i/4e3,X=t,Y=Z=d=1;++Z<w&(Y<6-(32<Z&27<X%w&&X/9^Z/8)*8%46||d|(s=(X&Y&Z)%3/Z,a=b=1,d=Z/w));Y-=b)X+=a',t=9)> pic.twitter.com/N3WElPqtMY

— Frank Force 🌻 (@KilledByAPixel) April 22, 2022
\"\"\"","output":"","context":{"code":"nbRawHtml: \"\"\"

A City in a Bottle 🌆

<canvas style=width:99% id=c onclick=setInterval('for(c.width=w=99,++t,i=6e3;i--;c.getContext`2d`.fillRect(i%w,i/w|0,1-d*Z/w+s,1))for(a=i%w/50-1,s=b=1-i/4e3,X=t,Y=Z=d=1;++Z<w&(Y<6-(32<Z&27<X%w&&X/9^Z/8)*8%46||d|(s=(X&Y&Z)%3/Z,a=b=1,d=Z/w));Y-=b)X+=a',t=9)> pic.twitter.com/N3WElPqtMY

— Frank Force 🌻 (@KilledByAPixel) April 22, 2022
\"\"\"","output":""}},{"command":"nbRawHtml","code":"","output":"

A City in a Bottle 🌆

<canvas style=width:99% id=c onclick=setInterval('for(c.width=w=99,++t,i=6e3;i--;c.getContext`2d`.fillRect(i%w,i/w|0,1-d*Z/w+s,1))for(a=i%w/50-1,s=b=1-i/4e3,X=t,Y=Z=d=1;++Z<w&(Y<6-(32<Z&27<X%w&&X/9^Z/8)*8%46||d|(s=(X&Y&Z)%3/Z,a=b=1,d=Z/w));Y-=b)X+=a',t=9)> pic.twitter.com/N3WElPqtMY

— Frank Force 🌻 (@KilledByAPixel) April 22, 2022
","context":{"code":"","output":"

A City in a Bottle 🌆

<canvas style=width:99% id=c onclick=setInterval('for(c.width=w=99,++t,i=6e3;i--;c.getContext`2d`.fillRect(i%w,i/w|0,1-d*Z/w+s,1))for(a=i%w/50-1,s=b=1-i/4e3,X=t,Y=Z=d=1;++Z<w&(Y<6-(32<Z&27<X%w&&X/9^Z/8)*8%46||d|(s=(X&Y&Z)%3/Z,a=b=1,d=Z/w));Y-=b)X+=a',t=9)> pic.twitter.com/N3WElPqtMY

— Frank Force 🌻 (@KilledByAPixel) April 22, 2022
"}},{"command":"nbText","code":"","output":"And we can use `nbRawHtml` also to visualize the canvas code. The only adjustment I made is to add a solid border. Click inside to start the animation.","context":{"code":"","output":"And we can use `nbRawHtml` also to visualize the canvas code. The only adjustment I made is to add a solid border. Click inside to start the animation."}},{"command":"nbRawHtml","code":"","output":"","context":{"code":"","output":""}},{"command":"nbText","code":"","output":"Not sure why, but after you click a big white vertical area is created.\n\nAnyhoo, enjoy another tweet that links to a great Observable document that explains the code\n(I guess that now we should be able to reproduce that observable document in nimib...):\n","context":{"code":"","output":"Not sure why, but after you click a big white vertical area is created.\n\nAnyhoo, enjoy another tweet that links to a great Observable document that explains the code\n(I guess that now we should be able to reproduce that observable document in nimib...):"}},{"command":"nbRawHtml","code":"","output":"

If you'd like to learn how the code works, I recommend checking out this amazing breakdown by @DanielDarabos that reorganizes and comments the code and provides controls to play with it.https://t.co/SVtC0yF0gJ

— Frank Force 🌻 (@KilledByAPixel) May 26, 2022
","context":{"code":"","output":"

If you'd like to learn how the code works, I recommend checking out this amazing breakdown by @DanielDarabos that reorganizes and comments the code and provides controls to play with it.https://t.co/SVtC0yF0gJ

— Frank Force 🌻 (@KilledByAPixel) May 26, 2022
"}},{"command":"nbText","code":"","output":"And actually this content was inspired by the following tweet exchange that brought back in my timeline the amazing city in a bottle:","context":{"code":"","output":"And actually this content was inspired by the following tweet exchange that brought back in my timeline the amazing city in a bottle:"}},{"command":"nbRawHtml","code":"","output":"

It has its limits! I tried running @KilledByAPixel's amazing City in a Bottle tweet-length shader through it, and GPT-3 replied with the equivalent of a ¯\\_(ツ)_/¯ https://t.co/n1c4oNEwNC pic.twitter.com/FeYixu7JS6

— Andy Baio (@waxpancake) July 11, 2022
","context":{"code":"","output":"

It has its limits! I tried running @KilledByAPixel's amazing City in a Bottle tweet-length shader through it, and GPT-3 replied with the equivalent of a ¯\\_(ツ)_/¯ https://t.co/n1c4oNEwNC pic.twitter.com/FeYixu7JS6

— Andy Baio (@waxpancake) July 11, 2022
"}},{"command":"nbText","code":"","output":"Do I like Twitter? Oh, boy...","context":{"code":"","output":"Do I like Twitter? Oh, boy..."}},{"command":"nbRawHtml","code":"","output":"

Oh, boy! West Coast, #YoungSheldon starts now! pic.twitter.com/bpSn7NySME

— Young Sheldon (@YoungSheldon) January 4, 2019
","context":{"code":"","output":"

Oh, boy! West Coast, #YoungSheldon starts now! pic.twitter.com/bpSn7NySME

— Young Sheldon (@YoungSheldon) January 4, 2019
"}}] +} \ No newline at end of file diff --git a/drafts/arraymancer_tutorial.html b/drafts/arraymancer_tutorial.html deleted file mode 100644 index 70469e1..0000000 --- a/drafts/arraymancer_tutorial.html +++ /dev/null @@ -1,356 +0,0 @@ - - - - drafts\arraymancer_tutorial.nim - - - - - - - - - - - -
-
- 🏡 - drafts\arraymancer_tutorial.nim - -
-
-
-

Arraymancer Tutorial - First steps

-
-

A remake of the original tutorial using nimib: https://mratsim.github.io/Arraymancer/tuto.first_steps.html

-

I will note differences with the original in quoted sections.

-
-

Tensor properties

-

Tensors have the following properties:

-
    -
  • rank: 0 for scalar (cannot be stored), 1 for vector, 2 for matrices, N for N dimensional arrays
  • -
  • shape: a sequence of the tensor dimensions along each axis
  • -
-

Next properties are technical and there for completeness:

-
    -
  • stride: a sequence of numbers of steps to get the next item along a dimension
  • -
  • offset: the first element of the tensor
  • -
-
import
-  arraymancer, sugar, sequtils
-
-let d = [[1, 2, 3], [4, 5, 6]].toTensor()
-echo d
-
Tensor[system.int] of shape [2, 3]" on backend "Cpu"
-|1	2	3|
-|4	5	6|
-
-
-
-

message changed, it was: Tensor of shape 2x3 of type "int" on backend "Cpu"

-
-
dump d.rank
-dump d.shape
-dump d.strides               ## [x,y] => next row is x elements away in memory while next column is 1 element away.
-dump d.offset
-
d.rank = 2
-d.shape = [2, 3]
-d.strides = [3, 1]
-d.offset = 0
-
-
-

echo of shape and strides changed (dropped @)

-
-

Tensor creation

-

The canonical way to initialize a tensor is by converting a seq of seq of ... or an array of array of ... -into a tensor using toTensor. -toTensor supports deep nested sequences and arrays, even sequences of array of sequences.

-
let c = [[[1, 2, 3], [4, 5, 6]], [[11, 22, 33], [44, 55, 66]],
-         [[111, 222, 333], [444, 555, 666]],
-         [[1111, 2222, 3333], [4444, 5555, 6666]]].toTensor()
-echo c
-
Tensor[system.int] of shape [4, 2, 3]" on backend "Cpu"
-| | 	1	2	3 | 	11	22	33 | 	111	222	333 | 	1111	2222	3333|
-| | 	4	5	6 | 	44	55	66 | 	444	555	666 | 	4444	5555	6666|
-
-
-
-

I am not sure where the additional pipes come from, maybe a bug?

-
-

newTensor procedure can be used to initialize a tensor of a specific -shape with a default value. (0 for numbers, false for bool...)

-

zeros and ones procedures create a new tensor filled with 0 and -1 respectively.

-

zeros_like and ones_like take an input tensor and output a -tensor of the same shape but filled with 0 and 1 respectively.

-
let e = newTensor[bool]([2, 3])
-dump e
-
e = Tensor[system.bool] of shape [2, 3]" on backend "Cpu"
-|false	false	false|
-|false	false	false|
-
-
-
let f = zeros[float]([4, 3])
-dump f
-
f = Tensor[system.float] of shape [4, 3]" on backend "Cpu"
-|0.0	0.0	0.0|
-|0.0	0.0	0.0|
-|0.0	0.0	0.0|
-|0.0	0.0	0.0|
-
-
-
let g = ones[float]([4, 3])
-dump g
-
g = Tensor[system.float] of shape [4, 3]" on backend "Cpu"
-|1.0	1.0	1.0|
-|1.0	1.0	1.0|
-|1.0	1.0	1.0|
-|1.0	1.0	1.0|
-
-
-
let tmp = [[1, 2], [3, 4]].toTensor()
-let h = tmp.zeros_like
-dump h
-
h = Tensor[system.int] of shape [2, 2]" on backend "Cpu"
-|0	0|
-|0	0|
-
-
-
let i = tmp.ones_like
-dump i
-
i = Tensor[system.int] of shape [2, 2]" on backend "Cpu"
-|1	1|
-|1	1|
-
-
-

Accessing and modifying a value

-

Tensors value can be retrieved or set with array brackets.

-
var a = toSeq(1 .. 24).toTensor().reshape(2, 3, 4)
-echo a
-
Tensor[system.int] of shape [2, 3, 4]" on backend "Cpu"
-| | 	1	2	3	4 | 	13	14	15	16|
-| | 	5	6	7	8 | 	17	18	19	20|
-| | 	9	10	11	12 | 	21	22	23	24|
-
-
-
dump a[1, 1, 1]
-echo a
-
a[1, 1, 1] = 18
-Tensor[system.int] of shape [2, 3, 4]" on backend "Cpu"
-| | 	1	2	3	4 | 	13	14	15	16|
-| | 	5	6	7	8 | 	17	18	19	20|
-| | 	9	10	11	12 | 	21	22	23	24|
-
-
-
a[1, 1, 1] = 999
-echo a
-
Tensor[system.int] of shape [2, 3, 4]" on backend "Cpu"
-| | 	1	2	3	4 | 	13	14	15	16|
-| | 	5	6	7	8 | 	17	999	19	20|
-| | 	9	10	11	12 | 	21	22	23	24|
-
-
-

Copying

-

Warning ⚠: When you do the following, both tensors a and b will share data. -Full copy must be explicitly requested via the clone function.

-
let a = toSeq(1 .. 24).toTensor().reshape(2, 3, 4)
-var b = a
-var c = clone(a)
-

Here modifying b WILL modify a.

-
-

adding an example of modification and an example of clone:

-
-
dump a[1, 0, 0]
-c[1, 0, 0] = 0
-dump a[1, 0, 0]
-b[1, 0, 0] = 0
-dump a[1, 0, 0]
-
a[1, 0, 0] = 13
-a[1, 0, 0] = 13
-a[1, 0, 0] = 0
-
-

This behaviour is the same as Numpy and Julia, -reasons can be found in the following -under the hood article.

- -
- -
-
import nimib
-# I want to use this notebook also to show how one can customzie the nbCode block output
-# (to have output shown as comments) and also possibly to stitch together subsequent code samples
-# (I should use a render change in nbDoc). Probably I should do this after rendering refactoring.
-nbInit
-nbText: """
-# Arraymancer Tutorial - First steps
-
-> A remake of the original tutorial using nimib: <https://mratsim.github.io/Arraymancer/tuto.first_steps.html>
->
-> I will note differences with the original in quoted sections.
-
-## Tensor properties
-
-Tensors have the following properties:
-- `rank`: 0 for scalar (cannot be stored), 1 for vector, 2 for matrices, *N* for *N* dimensional arrays
-- `shape`: a sequence of the tensor dimensions along each axis
-
-Next properties are technical and there for completeness:
-- `stride`: a sequence of numbers of steps to get the next item along a dimension
-- `offset`: the first element of the tensor
-"""
-# order of variable names (d, c, e, ..., a, b) I guess it reflects the original order of the sections.
-nbCode:
-  import arraymancer, sugar, sequtils
-
-  let d = [[1, 2, 3], [4, 5, 6]].toTensor()
-
-  echo d
-
-nbText: """> message changed, it was: `Tensor of shape 2x3 of type "int" on backend "Cpu"`"""
-
-nbCode:
-  dump d.rank
-  dump d.shape
-  dump d.strides ## [x,y] => next row is x elements away in memory while next column is 1 element away.
-  dump d.offset
-nbText: "> echo of shape and strides changed (dropped @)"
-
-nbText: """
-## Tensor creation
-The canonical way to initialize a tensor is by converting a seq of seq of ... or an array of array of ...
-into a tensor using `toTensor`.
-`toTensor` supports deep nested sequences and arrays, even sequences of array of sequences.
-"""
-
-nbCode:
-  let c = [
-            [
-              [1,2,3],
-              [4,5,6]
-            ],
-            [
-              [11,22,33],
-              [44,55,66]
-            ],
-            [
-              [111,222,333],
-              [444,555,666]
-            ],
-            [
-              [1111,2222,3333],
-              [4444,5555,6666]
-            ]
-          ].toTensor()
-  echo c
-nbText: "> I am not sure where the additional pipes come from, maybe a bug?"
-nbText: """
-`newTensor` procedure can be used to initialize a tensor of a specific
-shape with a default value. (0 for numbers, false for bool...)
-
-`zeros` and `ones` procedures create a new tensor filled with 0 and
-1 respectively.
-
-`zeros_like` and `ones_like` take an input tensor and output a
-tensor of the same shape but filled with 0 and 1 respectively.
-"""
-nbCode:
-  let e = newTensor[bool]([2, 3])
-  dump e
-nbCode:
-  let f = zeros[float]([4, 3])
-  dump f
-nbCode:
-  let g = ones[float]([4, 3])
-  dump g
-nbCode:
-  let tmp = [[1,2],[3,4]].toTensor()
-  let h = tmp.zeros_like
-  dump h
-nbCode:
-  let i = tmp.ones_like
-  dump i
-
-nbText: """
-## Accessing and modifying a value
-
-Tensors value can be retrieved or set with array brackets.
-"""
-# need to import sequtils to have toSeq
-nbCode:
-    var a = toSeq(1..24).toTensor().reshape(2,3,4)
-    echo a
-nbCode:
-    dump a[1, 1, 1]
-    echo a
-nbCode:
-    a[1, 1, 1] = 999
-    echo a
-nbText: """
-## Copying
-
-Warning ⚠: When you do the following, both tensors `a` and `b` will share data.
-Full copy must be explicitly requested via the `clone` function.
-"""
-block: # using a block I can reuse a
-  nbCode:
-    let a = toSeq(1..24).toTensor().reshape(2,3,4)
-    var b = a
-    var c = clone(a)
-  nbText: """
-  Here modifying `b` WILL modify `a`.
-
-  > adding an example of modification and an example of clone:
-  """
-  # still in block scope in order to reuse b
-  nbCode:
-    dump a[1, 0, 0]
-    c[1, 0, 0] = 0
-    dump a[1, 0, 0]
-    b[1, 0, 0] = 0
-    dump a[1, 0, 0]
-nbText: """
-This behaviour is the same as Numpy and Julia,
-reasons can be found in the following
-[under the hood article](https://mratsim.github.io/Arraymancer/uth.copy_semantics.html).
-"""
-nbShow
-
- \ No newline at end of file diff --git a/drafts/city_in_a_bottle.html b/drafts/city_in_a_bottle.html deleted file mode 100644 index 4adbc82..0000000 --- a/drafts/city_in_a_bottle.html +++ /dev/null @@ -1,114 +0,0 @@ - - - - drafts/city_in_a_bottle.nim - - - - - - - - - - - -
-
- 🏡 - drafts/city_in_a_bottle.nim - -
-
-
-

City in a bottle

-
-

example of usage of nbRawOutput

-
-

How do I embed a tweet like this great tweet in nimib?

-

Easy! Just go in the top right corner of the tweet (⋮), click on "Embed Tweet", copy the code -and use nbRawOutput (see source by clicking "Show Source" button below)

- -

And we can use nbRawOutput also to visualize the canvas code. The only adjustment I made is to add a solid border. Click inside to start the animation.

- -

Not sure why, but after you click a big white vertical area is created.

-

Anyhoo, enjoy another tweet that links to a great Observable document that explains the code -(I guess that now we should be able to reproduce that observable document in nimib...):

- -

And actually this content was inspired by the following tweet exchange that brought back in my timeline the amazing city in a bottle:

- -

Do I like Twitter? Oh, boy...

- -
- -
-
import nimib
-
-nbInit
-
-nbText: """# City in a bottle
-> example of usage of `nbRawOutput`
-
-How do I embed a tweet like [this great tweet](https://twitter.com/killedbyapixel/status/1517294627996545024) in nimib?
-
-Easy! Just go in the top right corner of the tweet (⋮), click on "Embed Tweet", copy the code
-and use `nbRawOutput` (see source by clicking "Show Source" button below)
-"""
-nbRawOutput: """<blockquote class="twitter-tweet"><p lang="en" dir="ltr">A City in a Bottle 🌆<br><br>&lt;canvas style=width:99% id=c onclick=setInterval(&#39;for(c.width=w=99,++t,i=6e3;i--;c.getContext`2d`.fillRect(i%w,i/w|0,1-d*Z/w+s,1))for(a=i%w/50-1,s=b=1-i/4e3,X=t,Y=Z=d=1;++Z&lt;w&amp;(Y&lt;6-(32&lt;Z&amp;27&lt;X%w&amp;&amp;X/9^Z/8)*8%46||d|(s=(X&amp;Y&amp;Z)%3/Z,a=b=1,d=Z/w));Y-=b)X+=a&#39;,t=9)&gt; <a href="https://t.co/N3WElPqtMY">pic.twitter.com/N3WElPqtMY</a></p>&mdash; Frank Force 🌻 (@KilledByAPixel) <a href="https://twitter.com/KilledByAPixel/status/1517294627996545024?ref_src=twsrc%5Etfw">April 22, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>"""
-
-nbText: "And we can use `nbRawOutput` also to visualize the canvas code. The only adjustment I made is to add a solid border. Click inside to start the animation."
-nbRawOutput: """<canvas style="width:99%; border:1px solid #000000;" id=c onclick=setInterval('for(c.width=w=99,++t,i=6e3;i--;c.getContext`2d`.fillRect(i%w,i/w|0,1-d*Z/w+s,1))for(a=i%w/50-1,s=b=1-i/4e3,X=t,Y=Z=d=1;++Z<w&(Y<6-(32<Z&27<X%w&&X/9^Z/8)*8%46||d|(s=(X&Y&Z)%3/Z,a=b=1,d=Z/w));Y-=b)X+=a',t=9)></canvas>"""
-
-nbText: """Not sure why, but after you click a big white vertical area is created.
-
-Anyhoo, enjoy another tweet that links to a great Observable document that explains the code
-(I guess that now we should be able to reproduce that observable document in nimib...):
-"""
-nbRawOutput: """<blockquote class="twitter-tweet"><p lang="en" dir="ltr">If you&#39;d like to learn how the code works, I recommend checking out this amazing breakdown by <a href="https://twitter.com/DanielDarabos?ref_src=twsrc%5Etfw">@DanielDarabos</a> that reorganizes and comments the code and provides controls to play with it.<a href="https://t.co/SVtC0yF0gJ">https://t.co/SVtC0yF0gJ</a></p>&mdash; Frank Force 🌻 (@KilledByAPixel) <a href="https://twitter.com/KilledByAPixel/status/1529838360235220992?ref_src=twsrc%5Etfw">May 26, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>"""
-
-nbText: "And actually this content was inspired by the following tweet exchange that brought back in my timeline the amazing city in a bottle:"
-nbRawOutput: """<blockquote class="twitter-tweet"><p lang="en" dir="ltr">It has its limits! I tried running <a href="https://twitter.com/KilledByAPixel?ref_src=twsrc%5Etfw">@KilledByAPixel</a>&#39;s amazing City in a Bottle tweet-length shader through it, and GPT-3 replied with the equivalent of a ¯\_(ツ)_/¯ <a href="https://t.co/n1c4oNEwNC">https://t.co/n1c4oNEwNC</a> <a href="https://t.co/FeYixu7JS6">pic.twitter.com/FeYixu7JS6</a></p>&mdash; Andy Baio (@waxpancake) <a href="https://twitter.com/waxpancake/status/1546634381183164416?ref_src=twsrc%5Etfw">July 11, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>"""
-
-nbText: "Do I like Twitter? Oh, boy..."
-nbRawOutput: """<blockquote class="twitter-tweet"><p lang="en" dir="ltr">Oh, boy! West Coast, <a href="https://twitter.com/hashtag/YoungSheldon?src=hash&amp;ref_src=twsrc%5Etfw">#YoungSheldon</a> starts now! <a href="https://t.co/bpSn7NySME">pic.twitter.com/bpSn7NySME</a></p>&mdash; Young Sheldon (@YoungSheldon) <a href="https://twitter.com/YoungSheldon/status/1081045053798273024?ref_src=twsrc%5Etfw">January 4, 2019</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>"""
-
-nbSave
-
- \ No newline at end of file diff --git a/drafts/city_in_a_bottle.nim b/drafts/city_in_a_bottle.nim deleted file mode 100644 index 906fbf5..0000000 --- a/drafts/city_in_a_bottle.nim +++ /dev/null @@ -1,31 +0,0 @@ -import nimib - -nbInit - -nbText: """# City in a bottle -> example of usage of `nbRawOutput` - -How do I embed a tweet like [this great tweet](https://twitter.com/killedbyapixel/status/1517294627996545024) in nimib? - -Easy! Just go in the top right corner of the tweet (⋮), click on "Embed Tweet", copy the code -and use `nbRawOutput` (see source by clicking "Show Source" button below) -""" -nbRawOutput: """ """ - -nbText: "And we can use `nbRawOutput` also to visualize the canvas code. The only adjustment I made is to add a solid border. Click inside to start the animation." -nbRawOutput: """""" - -nbText: """Not sure why, but after you click a big white vertical area is created. - -Anyhoo, enjoy another tweet that links to a great Observable document that explains the code -(I guess that now we should be able to reproduce that observable document in nimib...): -""" -nbRawOutput: """ """ - -nbText: "And actually this content was inspired by the following tweet exchange that brought back in my timeline the amazing city in a bottle:" -nbRawOutput: """ """ - -nbText: "Do I like Twitter? Oh, boy..." -nbRawOutput: """ """ - -nbSave \ No newline at end of file diff --git a/head_other.mustache b/head_other.mustache deleted file mode 100644 index 4384cea..0000000 --- a/head_other.mustache +++ /dev/null @@ -1 +0,0 @@ - diff --git a/nblog.nimble b/nblog.nimble index f09aa4b..baa8b47 100644 --- a/nblog.nimble +++ b/nblog.nimble @@ -1 +1,14 @@ -# just a placeholder to help nimib get a HomeDir (but it should fall back on git repo!) \ No newline at end of file +# Package + +version = "0.1.0" +author = "Pietro Peterlongo" +description = "simple nimib blog generator" +license = "MIT" +srcDir = "src" +bin = @["nblog"] + +# Deps + +requires "nim >= 1.4.0" +requires "nimib >= 1.3.0" +requires "climate >= 1.0.2" \ No newline at end of file diff --git a/nimib.toml b/nimib.toml index 82681fb..0837b5a 100644 --- a/nimib.toml +++ b/nimib.toml @@ -1,3 +1,3 @@ [nimib] -srcDir = "." -homeDir = "." \ No newline at end of file +srcDir = "posts" +homeDir = "docs" diff --git a/drafts/arraymancer_tutorial.nim b/posts/arraymancer_tutorial.nim similarity index 88% rename from drafts/arraymancer_tutorial.nim rename to posts/arraymancer_tutorial.nim index 0bb8b4f..8ac2e8c 100644 --- a/drafts/arraymancer_tutorial.nim +++ b/posts/arraymancer_tutorial.nim @@ -1,12 +1,13 @@ import nimib -# I want to use this notebook also to show how one can customzie the nbCode block output -# (to have output shown as comments) and also possibly to stitch together subsequent code samples -# (I should use a render change in nbDoc). Probably I should do this after rendering refactoring. -nbInit -nbText: """ -# Arraymancer Tutorial - First steps +import nblog + +nbInit(theme = useNblog) +nb.title "Arraymancer Tutorial - First steps" +nb.subtitle "A remake of the original tutorial using nimib" +nb.pubDate 2021, 3, 12 -> A remake of the original tutorial using nimib: +nbText: """ +> original tutorial: > > I will note differences with the original in quoted sections. @@ -135,4 +136,5 @@ This behaviour is the same as Numpy and Julia, reasons can be found in the following [under the hood article](https://mratsim.github.io/Arraymancer/uth.copy_semantics.html). """ -nbShow \ No newline at end of file + +nbSaveJson diff --git a/posts/city_in_a_bottle.nim b/posts/city_in_a_bottle.nim new file mode 100644 index 0000000..fb69aef --- /dev/null +++ b/posts/city_in_a_bottle.nim @@ -0,0 +1,34 @@ +import nimib +import nblog + +nbInit(theme = useNblog) +nb.title "City in a bottle" +nb.subtitle "How to embed a tweet in a nimib document" +nb.pubDate 2022, 7, 12 + +nbText: """ +How do I embed a tweet like [this great tweet](https://twitter.com/killedbyapixel/status/1517294627996545024) in nimib? + +Easy! Just go in the top right corner of the tweet (⋮), click on "Embed Tweet", copy the code +and use `nbRawHtml`: +""" +nimibCode: + nbRawHtml: """ """ + +nbText: "And we can use `nbRawHtml` also to visualize the canvas code. The only adjustment I made is to add a solid border. Click inside to start the animation." +nbRawHtml: """""" + +nbText: """Not sure why, but after you click a big white vertical area is created. + +Anyhoo, enjoy another tweet that links to a great Observable document that explains the code +(I guess that now we should be able to reproduce that observable document in nimib...): +""" +nbRawHtml: """ """ + +nbText: "And actually this content was inspired by the following tweet exchange that brought back in my timeline the amazing city in a bottle:" +nbRawHtml: """ """ + +nbText: "Do I like Twitter? Oh, boy..." +nbRawHtml: """ """ + +nbSaveJson \ No newline at end of file diff --git a/posts/config.nims b/posts/config.nims new file mode 100644 index 0000000..3bb69f8 --- /dev/null +++ b/posts/config.nims @@ -0,0 +1 @@ +switch("path", "$projectDir/../src") \ No newline at end of file diff --git a/src/nblog.nim b/src/nblog.nim new file mode 100644 index 0000000..a510ab0 --- /dev/null +++ b/src/nblog.nim @@ -0,0 +1,196 @@ +import std / [os, osproc, times] +import nimib / themes +import nimib except toJson +import mustachepkg / values +import std / strformat +import std / strutils +import jsony +import json + +type + Date* = object + year*: int + month*: range[1..12] + day*: range[1..31] + +proc useNblog*(nb: var NbDoc) = + nb.useDefault + nb.templateDirs = @["../templates"] + +proc title*(nb: var NbDoc, title: string) = + nb.context["title"] = title + nbText: "# " & title + +proc subtitle*(nb: var NbDoc, subtitle: string) = + nb.context["title"] = nb.context["title"].castStr & " - " & subtitle + nbText: "## " & subtitle + +proc castValue*(date: Date): Value = + let newValue = new(Table[string, Value]) + result = Value(kind: vkTable, vTable: newValue) + newValue["year"] = date.year.castValue + newValue["month"] = date.month.castValue + newValue["day"] = date.day.castValue + +proc castDate*(value: Value): Date = + assert value.kind == vkTable + assert value.vTable["year"].kind == vkInt + result.year = int(value.vTable["year"].vInt) + assert value.vTable["month"].kind == vkInt + result.month = int(value.vTable["month"].vInt) + assert value.vTable["day"].kind == vkInt + result.day = int(value.vTable["day"].vInt) + +proc toMon(month: range[1..12]): string = + ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"][month - 1] + +proc pubDate*(nb: var NbDoc; year, month, day: int) = + nb.context["pub_date"] = Date(year: year, month: month, day: day) + let datetime = &"{year}-{month}-{day}" + let date = &"{toMon(month)} {day:02}, {year}" + nbRawHtml: &"""

""" + +proc dumpHook*(s: var string, v: Value) + +proc dumpHook*(s: var string, v: Value) = + case v.kind: + of vkInt: s.add $(v.vInt) + of vkFloat32: s.add $(v.vFloat32) + of vkFloat64: s.add $(v.vFloat64) + of vkString: s.add jsony.toJson(v.vString) + of vkBool: s.add jsony.toJson(v.vBool) + of vkProc: discard + of vkSeq: s.add jsony.toJson(v.vSeq) + of vkTable: s.add jsony.toJson(v.vTable[]) + +proc dumpHook*(s: var string, context: Context) = + s.add jsony.toJson(context.values) + +proc dumpHook*(s: var string, nb: NbDoc) = + s.add "{\n" + s.add " \"data\": " & jsony.toJson(nb.context) & ",\n" + s.add " \"blocks\": " & jsony.toJson(nb.blocks) & "\n" + s.add "}" + +template nbSaveJson* = + nb.nbCollectAllNbJs() + writeFile(nb.filename.replace(".html", ".json"), jsony.toJson(nb)) + +proc parseHook*(s: string, i: var int, v: var Context) = + var data: JsonNode + parseHook(s, i, data) + v = newContext(values = data.toValues) + +type + SimplifiedNbDoc = object + data: Context + blocks: seq[NbBlock] + +proc parseHook*(s: string, i: var int, v: var NbDoc) = + var simpleNb: SimplifiedNbDoc + parseHook(s, i, simpleNb) + v.context = simpleNb.data + v.blocks = simpleNb.blocks + +proc nbJsonToHtml*(filename: string) = + nbInit(theme=useNblog) + let nbJson = readFile(filename) + let nb2 = jsony.fromJson(nbJson, NbDoc) + nb.context = nb2.context + nb.blocks = nb2.blocks + nb.filename = filename.replace(".json", ".html") #todo: replace with a proper changeExt + nbSave + +proc build*(post: string, createJson = false): bool = + # todo: do not hardcode all folders (and do not assume we are in docs) + # I should split the build command in first building json + # later building html (after reading all json) + # although for nblog I need it only for index + echo "[nblog] building " & post + let postJson = post & ".json" + if not postJson.fileExists or createJson: + echo "[nblog] creating " & postJson + let postSrc = "../posts/" & post & ".nim" + if not postSrc.fileExists: + echo "[nblog.error] source not found: " & postSrc + return + let runCmd = "nim r " & postSrc + let exitCode = execCmd runCmd + if exitCode != 0: + echo "[nblog.error] error while running source: " & runCmd + return + if not postJson.fileExists: + echo "[nblog.error] json not created: " & postJson + nbJsonToHtml(postJson) + return true + +proc init*(post: string): bool = + let postSrc = "../posts/" & post & ".nim" + if postSrc.fileExists: + echo "[nblog.error] files already exists: " & postSrc + return + let today = times.now() + postSrc.writeFile(fmt""" +import nimib +import nblog + +nbInit(theme = useNblog) +nb.title "{post}" +nb.subtitle "tell me more about {post}" +nb.pubDate {today.year}, {ord(today.month)}, {today.monthday} + +nbText: + "say something" + +nbCode: + echo "code something" + +nbSaveJson +""") + +when isMainModule: + import climate + nbInit # this puts me in docs folder + # todo: better mechanism to list all posts + let allPosts = @[ + "city_in_a_bottle", + "arraymancer_tutorial" + ] + + proc helpCmd(context: climate.Context): int = + echo """ +build build all posts +build this that build only posts "this" and "that" +build this --json force creation of json when building (in case it was already created) +init new_post init a new blogpost "new_post" +""" + + proc buildCmd(context: climate.Context): int = + let posts = block: + if context.cmdArguments.len == 0: + allPosts + else: + context.cmdArguments + let createJson = "createJson" in context.cmdOptions or "json" in context.cmdOptions + for post in posts: + if not build(post, createJson = createJson): + inc result + + proc initCmd(context: climate.Context): int = + let posts = block: + if context.cmdArguments.len == 0: + echo "todo: init a nblog" + @[] + else: + context.cmdArguments + for post in posts: + if not init(post): + inc result + + + quit parseCommands( + { + "build": buildCmd, + "init": initCmd, + }, + defaultHandler=helpCmd) \ No newline at end of file diff --git a/templates/head_other.mustache b/templates/head_other.mustache new file mode 100644 index 0000000..0c29274 --- /dev/null +++ b/templates/head_other.mustache @@ -0,0 +1,21 @@ + + \ No newline at end of file diff --git a/templates/header.mustache b/templates/header.mustache new file mode 100644 index 0000000..34bcf24 --- /dev/null +++ b/templates/header.mustache @@ -0,0 +1,11 @@ +
+
+ + {{> header_right }} +
+
+
\ No newline at end of file