Compare commits
19 commits
ef8ddb9025
...
4491c6dba1
Author | SHA1 | Date | |
---|---|---|---|
4491c6dba1 | |||
|
6a38a1cebc | ||
|
467f3c3a63 | ||
|
853e9ad50d | ||
|
27faf59549 | ||
|
d1ecb76cdc | ||
|
e86108e18f | ||
|
cef1bc256d | ||
|
761e4ef170 | ||
|
815f5c19f0 | ||
|
bf31305617 | ||
|
e1c70b9db0 | ||
|
d7ea1174ff | ||
|
eca9eb7f13 | ||
|
48204b039b | ||
|
7e927469dc | ||
|
b69fa2f2b1 | ||
|
8240f40719 | ||
|
98de1d24d6 |
27 changed files with 247 additions and 127 deletions
|
@ -1 +1 @@
|
|||
1.5.0
|
||||
1.8.1
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
nodejs 12.14.1
|
||||
nodejs 16.18.0
|
||||
crystal 1.5.0
|
||||
|
|
33
CHANGELOG
33
CHANGELOG
|
@ -1,3 +1,36 @@
|
|||
2023-05-06
|
||||
|
||||
* Upgrade to Lucky framework 1.0.0
|
||||
* Upgrade to Crystal version 1.8.1
|
||||
* If embedded media has a caption, it will now be displayed
|
||||
|
||||
2023-03-25
|
||||
|
||||
* Headings now have an ID so readers can link to a part if an article
|
||||
* If a URL contains `global-identity-2`, Scribe will now correctly parse the article ID.
|
||||
|
||||
2022-11-06
|
||||
|
||||
* Fix viewing articles if the URL has a trailing slash
|
||||
* Update to nodejs 16.18.0
|
||||
|
||||
2022-10-30
|
||||
|
||||
* Update to nodejs 16.18.0
|
||||
|
||||
2022-10-30
|
||||
|
||||
* Fix viewing articles if the URL has a trailing slash
|
||||
|
||||
2022-10-11
|
||||
|
||||
* Don't clip gist contents (CSS fix)
|
||||
|
||||
2022-09-24
|
||||
|
||||
* Replace Redirector extension with LibRedirect
|
||||
* Remove downloadable Redirector config
|
||||
|
||||
2022-07-19
|
||||
|
||||
* Fix downloadable config file for Redirector extension
|
||||
|
|
|
@ -5,5 +5,7 @@
|
|||
"https://scribe.bus-hit.me",
|
||||
"https://scribe.froth.zone",
|
||||
"https://scribe.esmailelbob.xyz",
|
||||
"https://scribe.privacydev.net"
|
||||
"https://scribe.privacydev.net",
|
||||
"https://scribe.rawbit.ninja",
|
||||
"https://sc.vern.cc"
|
||||
]
|
||||
|
|
|
@ -7,7 +7,11 @@
|
|||
* <https://scribe.froth.zone>
|
||||
* <https://scribe.esmailelbob.xyz>
|
||||
* <https://scribe.privacydev.net>
|
||||
* <scribe.lqs5fjmajyp7rvp4qvyubwofzi6d4imua7vs237rkc4m5qogitqwrgyd.onion> (Tor)
|
||||
* <https://scribe.rawbit.ninja>
|
||||
* <http://scribe.esmail5pdn24shtvieloeedh7ehz3nrwcdivnfhfcedl7gf4kwddhkqd.onion> (Tor)
|
||||
* <http://w7uhv5lxhgck72hhimdglmusc54t4m6bionlmd5mvyddq3bs53mohqid.onion> (Tor)
|
||||
* <http://scribe.g4c3eya4clenolymqbpgwz3q3tawoxw56yhzk4vugqrl6dtu3ejvhjid.onion> (Tor)
|
||||
* [sc.vern.i2p](http://vern3whzyfmjclq6snhlupma6nrmojghwp37tydfgqotj7sc6izq.b32.i2p) (I2P)
|
||||
|
||||
## How do I get my instance on this list?
|
||||
|
||||
|
|
40
shard.lock
40
shard.lock
|
@ -2,15 +2,15 @@ version: 2.0
|
|||
shards:
|
||||
authentic:
|
||||
git: https://github.com/luckyframework/authentic.git
|
||||
version: 0.8.2
|
||||
version: 1.0.0
|
||||
|
||||
avram:
|
||||
git: https://github.com/luckyframework/avram.git
|
||||
version: 0.23.0
|
||||
version: 1.0.0
|
||||
|
||||
backtracer:
|
||||
git: https://github.com/sija/backtracer.cr.git
|
||||
version: 1.2.1
|
||||
version: 1.2.2
|
||||
|
||||
cadmium_transliterator:
|
||||
git: https://github.com/cadmiumcr/transliterator.git
|
||||
|
@ -18,7 +18,7 @@ shards:
|
|||
|
||||
carbon:
|
||||
git: https://github.com/luckyframework/carbon.git
|
||||
version: 0.2.1
|
||||
version: 0.3.0
|
||||
|
||||
cry:
|
||||
git: https://github.com/luckyframework/cry.git
|
||||
|
@ -26,7 +26,7 @@ shards:
|
|||
|
||||
crystar:
|
||||
git: https://github.com/naqvis/crystar.git
|
||||
version: 0.2.0
|
||||
version: 0.2.0+git.commit.56db8bb9dfbd5ed6d7908353405a5fae632a6561
|
||||
|
||||
db:
|
||||
git: https://github.com/crystal-lang/crystal-db.git
|
||||
|
@ -38,15 +38,23 @@ shards:
|
|||
|
||||
exception_page:
|
||||
git: https://github.com/crystal-loot/exception_page.git
|
||||
version: 0.2.2
|
||||
version: 0.3.0
|
||||
|
||||
fnv:
|
||||
git: https://github.com/naqvis/crystal-fnv.git
|
||||
version: 0.1.3
|
||||
|
||||
habitat:
|
||||
git: https://github.com/luckyframework/habitat.git
|
||||
version: 0.4.7
|
||||
|
||||
html5:
|
||||
git: https://github.com/naqvis/crystal-html5.git
|
||||
version: 0.4.0
|
||||
|
||||
lucky:
|
||||
git: https://github.com/luckyframework/lucky.git
|
||||
version: 0.30.1
|
||||
version: 1.0.0
|
||||
|
||||
lucky_cache:
|
||||
git: https://github.com/luckyframework/lucky_cache.git
|
||||
|
@ -58,11 +66,11 @@ shards:
|
|||
|
||||
lucky_flow:
|
||||
git: https://github.com/luckyframework/lucky_flow.git
|
||||
version: 0.7.3
|
||||
version: 0.9.0
|
||||
|
||||
lucky_router:
|
||||
git: https://github.com/luckyframework/lucky_router.git
|
||||
version: 0.5.1
|
||||
version: 0.5.2
|
||||
|
||||
lucky_task:
|
||||
git: https://github.com/luckyframework/lucky_task.git
|
||||
|
@ -82,7 +90,7 @@ shards:
|
|||
|
||||
selenium:
|
||||
git: https://github.com/matthewmcgarvey/selenium.cr.git
|
||||
version: 0.9.1
|
||||
version: 0.10.0
|
||||
|
||||
shell-table:
|
||||
git: https://github.com/luckyframework/shell-table.cr.git
|
||||
|
@ -98,9 +106,17 @@ shards:
|
|||
|
||||
webdrivers:
|
||||
git: https://github.com/matthewmcgarvey/webdrivers.cr.git
|
||||
version: 0.4.0
|
||||
version: 0.4.1
|
||||
|
||||
webless:
|
||||
git: https://github.com/matthewmcgarvey/webless.git
|
||||
version: 0.1.0
|
||||
|
||||
wordsmith:
|
||||
git: https://github.com/luckyframework/wordsmith.git
|
||||
version: 0.3.0
|
||||
version: 0.4.0
|
||||
|
||||
xpath2:
|
||||
git: https://github.com/naqvis/crystal-xpath2.git
|
||||
version: 0.1.3
|
||||
|
||||
|
|
17
shard.yml
17
shard.yml
|
@ -8,27 +8,30 @@ targets:
|
|||
scribe:
|
||||
main: src/scribe.cr
|
||||
|
||||
crystal: 1.5.0
|
||||
crystal: 1.8.1
|
||||
|
||||
dependencies:
|
||||
lucky:
|
||||
github: luckyframework/lucky
|
||||
version: ~> 0.30.1
|
||||
version: ~> 1.0.0
|
||||
avram:
|
||||
github: luckyframework/avram
|
||||
version: ~> 1.0.0
|
||||
authentic:
|
||||
github: luckyframework/authentic
|
||||
version: ~> 0.8.2
|
||||
carbon:
|
||||
github: luckyframework/carbon
|
||||
version: ~> 0.2.0
|
||||
version: ~> 1.0.0
|
||||
lucky_env:
|
||||
github: luckyframework/lucky_env
|
||||
version: ~> 0.1.4
|
||||
lucky_task:
|
||||
github: luckyframework/lucky_task
|
||||
version: ~> 0.1.1
|
||||
carbon:
|
||||
github: luckyframework/carbon
|
||||
version: ~> 0.3.0
|
||||
monads:
|
||||
github: alex-lairan/monads
|
||||
development_dependencies:
|
||||
lucky_flow:
|
||||
github: luckyframework/lucky_flow
|
||||
version: ~> 0.7.3
|
||||
version: ~> 0.9.0
|
||||
|
|
|
@ -78,6 +78,14 @@ describe ArticleIdParser do
|
|||
result.should eq(Monads::Just.new("888888abcdef"))
|
||||
end
|
||||
|
||||
it "parses the post id for global identity 2 redirects" do
|
||||
request = resource_request("/m/global-identity-2?redirectUrl=https%3A%2F%2Fexample.com%2Fmy-post-999999abcdef")
|
||||
|
||||
result = ArticleIdParser.parse(request)
|
||||
|
||||
result.should eq(Monads::Just.new("999999abcdef"))
|
||||
end
|
||||
|
||||
it "returns Nothing if path is a username" do
|
||||
request = resource_request("/@ba5eba11")
|
||||
|
||||
|
|
|
@ -8,6 +8,7 @@ describe EmbeddedConverter do
|
|||
store = GistStore.new
|
||||
paragraph = PostResponse::Paragraph.from_json <<-JSON
|
||||
{
|
||||
"name": "ab12",
|
||||
"text": "",
|
||||
"type": "IFRAME",
|
||||
"href": null,
|
||||
|
@ -36,6 +37,44 @@ describe EmbeddedConverter do
|
|||
)
|
||||
)
|
||||
end
|
||||
|
||||
context "and a caption exists" do
|
||||
it "returns an EmbeddedContent node with caption" do
|
||||
store = GistStore.new
|
||||
paragraph = PostResponse::Paragraph.from_json <<-JSON
|
||||
{
|
||||
"name": "ab12",
|
||||
"text": "Caption",
|
||||
"type": "IFRAME",
|
||||
"href": null,
|
||||
"layout": "INSET_CENTER",
|
||||
"markups": [],
|
||||
"iframe": {
|
||||
"mediaResource": {
|
||||
"id": "abc123",
|
||||
"href": "https://twitter.com/user/status/1",
|
||||
"iframeSrc": "https://cdn.embedly.com/widgets/...",
|
||||
"iframeWidth": 500,
|
||||
"iframeHeight": 281
|
||||
}
|
||||
},
|
||||
"metadata": null
|
||||
}
|
||||
JSON
|
||||
caption = FigureCaption.new(children: [Text.new("Caption")] of Child)
|
||||
|
||||
result = EmbeddedConverter.convert(paragraph, store)
|
||||
|
||||
result.should eq(
|
||||
EmbeddedContent.new(
|
||||
src: "https://cdn.embedly.com/widgets/...",
|
||||
originalWidth: 500,
|
||||
originalHeight: 281,
|
||||
caption: caption,
|
||||
)
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when the mediaResource has a blank iframeSrc value" do
|
||||
|
@ -44,6 +83,7 @@ describe EmbeddedConverter do
|
|||
store = GistStore.new
|
||||
paragraph = PostResponse::Paragraph.from_json <<-JSON
|
||||
{
|
||||
"name": "ab12",
|
||||
"text": "",
|
||||
"type": "IFRAME",
|
||||
"href": null,
|
||||
|
@ -73,6 +113,7 @@ describe EmbeddedConverter do
|
|||
store = GistStore.new
|
||||
paragraph = PostResponse::Paragraph.from_json <<-JSON
|
||||
{
|
||||
"name": "ab12",
|
||||
"text": "",
|
||||
"type": "IFRAME",
|
||||
"href": null,
|
||||
|
|
|
@ -12,6 +12,7 @@ describe GistScanner do
|
|||
)
|
||||
paragraphs = [
|
||||
PostResponse::Paragraph.new(
|
||||
name: "ab12",
|
||||
text: "Check out this gist:",
|
||||
type: PostResponse::ParagraphType::P,
|
||||
markups: [] of PostResponse::Markup,
|
||||
|
@ -20,6 +21,7 @@ describe GistScanner do
|
|||
metadata: nil
|
||||
),
|
||||
PostResponse::Paragraph.new(
|
||||
name: "ab13",
|
||||
text: "",
|
||||
type: PostResponse::ParagraphType::IFRAME,
|
||||
markups: [] of PostResponse::Markup,
|
||||
|
@ -45,6 +47,7 @@ describe GistScanner do
|
|||
)
|
||||
paragraphs = [
|
||||
PostResponse::Paragraph.new(
|
||||
name: "ab12",
|
||||
text: "",
|
||||
type: PostResponse::ParagraphType::IFRAME,
|
||||
markups: [] of PostResponse::Markup,
|
||||
|
@ -78,6 +81,7 @@ describe GistScanner do
|
|||
)
|
||||
paragraphs = [
|
||||
PostResponse::Paragraph.new(
|
||||
name: "ab12",
|
||||
text: "",
|
||||
type: PostResponse::ParagraphType::IFRAME,
|
||||
markups: [] of PostResponse::Markup,
|
||||
|
@ -86,6 +90,7 @@ describe GistScanner do
|
|||
metadata: nil
|
||||
),
|
||||
PostResponse::Paragraph.new(
|
||||
name: "ab13",
|
||||
text: "",
|
||||
type: PostResponse::ParagraphType::IFRAME,
|
||||
markups: [] of PostResponse::Markup,
|
||||
|
|
|
@ -8,6 +8,7 @@ describe PageConverter do
|
|||
paragraph_json = <<-JSON
|
||||
[
|
||||
{
|
||||
"name": "ab12",
|
||||
"text": "#{title}",
|
||||
"type": "H3",
|
||||
"markups": [],
|
||||
|
@ -28,6 +29,7 @@ describe PageConverter do
|
|||
it "sets the author" do
|
||||
post_json = <<-JSON
|
||||
{
|
||||
"name": "ab12",
|
||||
"title": "This is a story",
|
||||
"createdAt": 0,
|
||||
"creator": {
|
||||
|
@ -52,6 +54,7 @@ describe PageConverter do
|
|||
it "sets the publish date/time" do
|
||||
post_json = <<-JSON
|
||||
{
|
||||
"name": "ab12",
|
||||
"title": "This is a story",
|
||||
"createdAt": 1000,
|
||||
"creator": {
|
||||
|
@ -77,6 +80,7 @@ describe PageConverter do
|
|||
paragraph_json = <<-JSON
|
||||
[
|
||||
{
|
||||
"name": "ab12",
|
||||
"text": "#{title}",
|
||||
"type": "H3",
|
||||
"markups": [],
|
||||
|
@ -85,6 +89,7 @@ describe PageConverter do
|
|||
"metadata": null
|
||||
},
|
||||
{
|
||||
"name": "ab12",
|
||||
"text": "Content",
|
||||
"type": "P",
|
||||
"markups": [],
|
||||
|
@ -117,6 +122,7 @@ def default_post_json(
|
|||
)
|
||||
<<-JSON
|
||||
{
|
||||
"name": "ab12",
|
||||
"title": "#{title}",
|
||||
"createdAt": 1628974309758,
|
||||
"creator": {
|
||||
|
|
|
@ -8,6 +8,7 @@ describe ParagraphConverter do
|
|||
paragraphs = Array(PostResponse::Paragraph).from_json <<-JSON
|
||||
[
|
||||
{
|
||||
"name": "ab12",
|
||||
"text": "Title",
|
||||
"type": "H3",
|
||||
"markups": [],
|
||||
|
@ -17,7 +18,7 @@ describe ParagraphConverter do
|
|||
}
|
||||
]
|
||||
JSON
|
||||
expected = [Heading3.new(children: [Text.new(content: "Title")] of Child)]
|
||||
expected = [Heading3.new(children: [Text.new(content: "Title")] of Child, identifier: "ab12")]
|
||||
|
||||
result = ParagraphConverter.new.convert(paragraphs, gist_store)
|
||||
|
||||
|
@ -29,6 +30,7 @@ describe ParagraphConverter do
|
|||
paragraphs = Array(PostResponse::Paragraph).from_json <<-JSON
|
||||
[
|
||||
{
|
||||
"name": "ab12",
|
||||
"text": "inline code",
|
||||
"type": "P",
|
||||
"markups": [
|
||||
|
@ -66,6 +68,7 @@ describe ParagraphConverter do
|
|||
paragraphs = Array(PostResponse::Paragraph).from_json <<-JSON
|
||||
[
|
||||
{
|
||||
"name": "ab12",
|
||||
"text": "One",
|
||||
"type": "ULI",
|
||||
"markups": [],
|
||||
|
@ -74,6 +77,7 @@ describe ParagraphConverter do
|
|||
"metadata": null
|
||||
},
|
||||
{
|
||||
"name": "ab13",
|
||||
"text": "Two",
|
||||
"type": "ULI",
|
||||
"markups": [],
|
||||
|
@ -82,6 +86,7 @@ describe ParagraphConverter do
|
|||
"metadata": null
|
||||
},
|
||||
{
|
||||
"name": "ab14",
|
||||
"text": "Not a list item",
|
||||
"type": "P",
|
||||
"markups": [],
|
||||
|
@ -109,6 +114,7 @@ describe ParagraphConverter do
|
|||
paragraphs = Array(PostResponse::Paragraph).from_json <<-JSON
|
||||
[
|
||||
{
|
||||
"name": "ab12",
|
||||
"text": "One",
|
||||
"type": "OLI",
|
||||
"markups": [],
|
||||
|
@ -117,6 +123,7 @@ describe ParagraphConverter do
|
|||
"metadata": null
|
||||
},
|
||||
{
|
||||
"name": "ab13",
|
||||
"text": "Two",
|
||||
"type": "OLI",
|
||||
"markups": [],
|
||||
|
@ -125,6 +132,7 @@ describe ParagraphConverter do
|
|||
"metadata": null
|
||||
},
|
||||
{
|
||||
"name": "ab14",
|
||||
"text": "Not a list item",
|
||||
"type": "P",
|
||||
"markups": [],
|
||||
|
@ -151,6 +159,7 @@ describe ParagraphConverter do
|
|||
gist_store = GistStore.new
|
||||
paragraph = PostResponse::Paragraph.from_json <<-JSON
|
||||
{
|
||||
"name": "ab12",
|
||||
"text": "Image by someuser",
|
||||
"type": "IMG",
|
||||
"markups": [
|
||||
|
@ -197,6 +206,7 @@ describe ParagraphConverter do
|
|||
paragraphs = Array(PostResponse::Paragraph).from_json <<-JSON
|
||||
[
|
||||
{
|
||||
"name": "ab12",
|
||||
"text": "text",
|
||||
"type": "H2",
|
||||
"markups": [],
|
||||
|
@ -205,6 +215,7 @@ describe ParagraphConverter do
|
|||
"metadata": null
|
||||
},
|
||||
{
|
||||
"name": "ab13",
|
||||
"text": "text",
|
||||
"type": "H3",
|
||||
"markups": [],
|
||||
|
@ -213,6 +224,7 @@ describe ParagraphConverter do
|
|||
"metadata": null
|
||||
},
|
||||
{
|
||||
"name": "ab14",
|
||||
"text": "text",
|
||||
"type": "H4",
|
||||
"markups": [],
|
||||
|
@ -221,6 +233,7 @@ describe ParagraphConverter do
|
|||
"metadata": null
|
||||
},
|
||||
{
|
||||
"name": "ab15",
|
||||
"text": "text",
|
||||
"type": "P",
|
||||
"markups": [],
|
||||
|
@ -229,6 +242,7 @@ describe ParagraphConverter do
|
|||
"metadata": null
|
||||
},
|
||||
{
|
||||
"name": "ab16",
|
||||
"text": "text",
|
||||
"type": "PRE",
|
||||
"markups": [],
|
||||
|
@ -237,6 +251,7 @@ describe ParagraphConverter do
|
|||
"metadata": null
|
||||
},
|
||||
{
|
||||
"name": "ab17",
|
||||
"text": "text",
|
||||
"type": "BQ",
|
||||
"markups": [],
|
||||
|
@ -245,6 +260,7 @@ describe ParagraphConverter do
|
|||
"metadata": null
|
||||
},
|
||||
{
|
||||
"name": "ab18",
|
||||
"text": "text",
|
||||
"type": "PQ",
|
||||
"markups": [],
|
||||
|
@ -253,6 +269,7 @@ describe ParagraphConverter do
|
|||
"metadata": null
|
||||
},
|
||||
{
|
||||
"name": "ab19",
|
||||
"text": "text",
|
||||
"type": "ULI",
|
||||
"markups": [],
|
||||
|
@ -261,6 +278,7 @@ describe ParagraphConverter do
|
|||
"metadata": null
|
||||
},
|
||||
{
|
||||
"name": "ab20",
|
||||
"text": "text",
|
||||
"type": "OLI",
|
||||
"markups": [],
|
||||
|
@ -269,6 +287,7 @@ describe ParagraphConverter do
|
|||
"metadata": null
|
||||
},
|
||||
{
|
||||
"name": "ab21",
|
||||
"text": "text",
|
||||
"type": "IMG",
|
||||
"markups": [],
|
||||
|
@ -281,6 +300,7 @@ describe ParagraphConverter do
|
|||
}
|
||||
},
|
||||
{
|
||||
"name": "ab22",
|
||||
"text": "",
|
||||
"type": "IFRAME",
|
||||
"markups": [],
|
||||
|
@ -296,6 +316,7 @@ describe ParagraphConverter do
|
|||
"metadata": null
|
||||
},
|
||||
{
|
||||
"name": "ab23",
|
||||
"text": "Mixtape",
|
||||
"type": "MIXTAPE_EMBED",
|
||||
"href": null,
|
||||
|
@ -317,9 +338,9 @@ describe ParagraphConverter do
|
|||
]
|
||||
JSON
|
||||
expected = [
|
||||
Heading1.new([Text.new("text")] of Child),
|
||||
Heading2.new([Text.new("text")] of Child),
|
||||
Heading3.new([Text.new("text")] of Child),
|
||||
Heading1.new([Text.new("text")] of Child, identifier: "ab12"),
|
||||
Heading2.new([Text.new("text")] of Child, identifier: "ab13"),
|
||||
Heading3.new([Text.new("text")] of Child, identifier: "ab14"),
|
||||
Paragraph.new([Text.new("text")] of Child),
|
||||
Preformatted.new([Text.new("text")] of Child),
|
||||
BlockQuote.new([Text.new("text")] of Child), # BQ
|
||||
|
|
|
@ -184,13 +184,13 @@ describe PageContent do
|
|||
nodes: [
|
||||
Heading1.new(children: [
|
||||
Text.new(content: "Title!"),
|
||||
] of Child),
|
||||
] of Child, identifier: "ab12"),
|
||||
] of Child
|
||||
)
|
||||
|
||||
html = PageContent.new(page: page).render_to_string
|
||||
|
||||
html.should eq %(<h1>Title!</h1>)
|
||||
html.should eq %(<h1 id="ab12">Title!</h1>)
|
||||
end
|
||||
|
||||
it "renders an H3" do
|
||||
|
@ -201,13 +201,13 @@ describe PageContent do
|
|||
nodes: [
|
||||
Heading2.new(children: [
|
||||
Text.new(content: "Title!"),
|
||||
] of Child),
|
||||
] of Child, identifier: "ab12"),
|
||||
] of Child
|
||||
)
|
||||
|
||||
html = PageContent.new(page: page).render_to_string
|
||||
|
||||
html.should eq %(<h2>Title!</h2>)
|
||||
html.should eq %(<h2 id="ab12">Title!</h2>)
|
||||
end
|
||||
|
||||
it "renders an H4" do
|
||||
|
@ -218,13 +218,13 @@ describe PageContent do
|
|||
nodes: [
|
||||
Heading3.new(children: [
|
||||
Text.new(content: "In Conclusion..."),
|
||||
] of Child),
|
||||
] of Child, identifier: "ab12"),
|
||||
] of Child
|
||||
)
|
||||
|
||||
html = PageContent.new(page: page).render_to_string
|
||||
|
||||
html.should eq %(<h3>In Conclusion...</h3>)
|
||||
html.should eq %(<h3 id="ab12">In Conclusion...</h3>)
|
||||
end
|
||||
|
||||
it "renders an image" do
|
||||
|
@ -249,6 +249,7 @@ describe PageContent do
|
|||
end
|
||||
|
||||
it "renders embedded content" do
|
||||
caption_children = [Text.new("Caption")] of Child
|
||||
page = Page.new(
|
||||
title: "Title",
|
||||
author: user_anchor_factory,
|
||||
|
@ -258,6 +259,7 @@ describe PageContent do
|
|||
src: "https://example.com",
|
||||
originalWidth: 1000,
|
||||
originalHeight: 600,
|
||||
caption: FigureCaption.new(children: caption_children)
|
||||
),
|
||||
] of Child
|
||||
)
|
||||
|
@ -268,6 +270,11 @@ describe PageContent do
|
|||
<figure>
|
||||
<iframe src="https://example.com" width="800" height="480" frameborder="0" allowfullscreen="true">
|
||||
</iframe>
|
||||
<label class="margin-toggle" for="#{caption_children.hash}">✍︎</label>
|
||||
<input class="margin-toggle" type="checkbox" id="#{caption_children.hash}">
|
||||
<span class="marginnote">
|
||||
Caption
|
||||
</span>
|
||||
</figure>
|
||||
HTML
|
||||
end
|
||||
|
|
|
@ -10,4 +10,4 @@ LuckyFlow.configure do |settings|
|
|||
# Be sure to disable for CI.
|
||||
# settings.driver = LuckyFlow::Drivers::Chrome
|
||||
end
|
||||
Spec.before_each { LuckyFlow::Server::INSTANCE.reset }
|
||||
LuckyFlow::Spec.setup
|
|
@ -2,6 +2,8 @@ ENV["LUCKY_ENV"] = "test"
|
|||
ENV["DEV_PORT"] = "5001"
|
||||
require "spec"
|
||||
require "lucky_flow"
|
||||
require "lucky_flow/ext/lucky"
|
||||
require "lucky_flow/ext/avram"
|
||||
require "../src/app"
|
||||
require "./support/flows/base_flow"
|
||||
require "./support/**"
|
||||
|
|
|
@ -1,56 +0,0 @@
|
|||
class RedirectionConfig::Index < Lucky::Action
|
||||
include Lucky::ProtectFromForgery
|
||||
include Lucky::EnforceUnderscoredRoute
|
||||
include Lucky::SecureHeaders::DisableFLoC
|
||||
|
||||
default_format :json
|
||||
|
||||
get "/redirection_config" do
|
||||
data(
|
||||
data: config_json,
|
||||
content_type: "application/json",
|
||||
disposition: "attachment",
|
||||
filename: "redirector-config.json"
|
||||
)
|
||||
end
|
||||
|
||||
private def config_json
|
||||
double_escaped_pattern = "^https?://(?:.*\\\\.)*(?<!(link\\\\.|cdn\\\\-images\\\\-\\\\d+\\\\.))medium\\\\.com(/.*)?$"
|
||||
<<-JSON
|
||||
{
|
||||
"createdBy": "Redirector v3.5.3",
|
||||
"createdAt": "2022-07-17T00:00:00.000Z",
|
||||
"redirects": [
|
||||
{
|
||||
"description": "Medium -> Scribe",
|
||||
"exampleUrl": "https://medium.com/@user/post-123456abcdef",
|
||||
"exampleResult": "https://#{app_domain}/@user/post-123456abcdef",
|
||||
"error": null,
|
||||
"includePattern": "#{double_escaped_pattern}",
|
||||
"excludePattern": "",
|
||||
"patternDesc": "",
|
||||
"redirectUrl": "https://#{app_domain}$2",
|
||||
"patternType": "R",
|
||||
"processMatches": "noProcessing",
|
||||
"disabled": false,
|
||||
"grouped": false,
|
||||
"appliesTo": [
|
||||
"main_frame",
|
||||
"sub_frame",
|
||||
"xmlhttprequest",
|
||||
"history",
|
||||
"other"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
JSON
|
||||
end
|
||||
|
||||
private def app_domain
|
||||
URI.parse(Home::Index.url).normalize
|
||||
.to_s
|
||||
.sub(/\/$/, "")
|
||||
.sub(/^https?:\/\//, "")
|
||||
end
|
||||
end
|
|
@ -1,7 +1,7 @@
|
|||
class ArticleIdParser
|
||||
include Monads
|
||||
|
||||
ID_REGEX = /[\/\-]([0-9a-f]+)$/i
|
||||
ID_REGEX = /[\/\-]([0-9a-f]+)\/?$/i
|
||||
|
||||
def self.parse(request : HTTP::Request)
|
||||
new.parse(request)
|
||||
|
@ -10,7 +10,7 @@ class ArticleIdParser
|
|||
def parse(request : HTTP::Request) : Maybe
|
||||
from_params = post_id_from_params(request.query_params)
|
||||
from_path = post_id_from_path(request.path)
|
||||
from_path.or(from_params)
|
||||
from_params.or(from_path)
|
||||
end
|
||||
|
||||
private def post_id_from_path(request_path : String)
|
||||
|
|
|
@ -34,11 +34,19 @@ class EmbeddedConverter
|
|||
EmbeddedContent.new(
|
||||
src: media.iframeSrc,
|
||||
originalWidth: media.iframeWidth,
|
||||
originalHeight: media.iframeHeight
|
||||
originalHeight: media.iframeHeight,
|
||||
caption: caption
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
private def caption : FigureCaption?
|
||||
if !paragraph.text.blank?
|
||||
children = [Text.new(paragraph.text || "")] of Child
|
||||
FigureCaption.new(children: children)
|
||||
end
|
||||
end
|
||||
|
||||
private def custom_embed(media : PostResponse::MediaResource) : Embedded
|
||||
if media.href.starts_with?(GIST_HOST_AND_SCHEME)
|
||||
GithubGist.new(href: media.href, gist_store: gist_store)
|
||||
|
|
|
@ -16,15 +16,15 @@ class ParagraphConverter
|
|||
when PostResponse::ParagraphType::H2
|
||||
paragraph = paragraphs.shift
|
||||
children = MarkupConverter.convert(paragraph.text, paragraph.markups)
|
||||
node = Heading1.new(children: children)
|
||||
node = Heading1.new(children: children, identifier: paragraph.name || "")
|
||||
when PostResponse::ParagraphType::H3
|
||||
paragraph = paragraphs.shift
|
||||
children = MarkupConverter.convert(paragraph.text, paragraph.markups)
|
||||
node = Heading2.new(children: children)
|
||||
node = Heading2.new(children: children, identifier: paragraph.name || "")
|
||||
when PostResponse::ParagraphType::H4
|
||||
paragraph = paragraphs.shift
|
||||
children = MarkupConverter.convert(paragraph.text, paragraph.markups)
|
||||
node = Heading3.new(children: children)
|
||||
node = Heading3.new(children: children, identifier: paragraph.name || "")
|
||||
when PostResponse::ParagraphType::IFRAME
|
||||
paragraph = paragraphs.shift
|
||||
node = EmbeddedConverter.convert(paragraph, gist_store)
|
||||
|
|
|
@ -27,6 +27,7 @@ class MediumClient
|
|||
content {
|
||||
bodyModel {
|
||||
paragraphs {
|
||||
name
|
||||
text
|
||||
type
|
||||
href
|
||||
|
|
|
@ -40,6 +40,9 @@ class PageContent < BaseComponent
|
|||
frameborder: "0",
|
||||
allowfullscreen: true,
|
||||
)
|
||||
if caption = child.caption
|
||||
render_child(caption)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -93,15 +96,15 @@ class PageContent < BaseComponent
|
|||
end
|
||||
|
||||
def render_child(node : Heading1)
|
||||
h1 { render_children(node.children) }
|
||||
h1(id: node.identifier) { render_children(node.children) }
|
||||
end
|
||||
|
||||
def render_child(node : Heading2)
|
||||
h2 { render_children(node.children) }
|
||||
h2(id: node.identifier) { render_children(node.children) }
|
||||
end
|
||||
|
||||
def render_child(node : Heading3)
|
||||
h3 { render_children(node.children) }
|
||||
h3(id: node.identifier) { render_children(node.children) }
|
||||
end
|
||||
|
||||
def render_child(child : Image)
|
||||
|
|
|
@ -7,13 +7,22 @@ p.meta {
|
|||
line-height: 1;
|
||||
}
|
||||
|
||||
.gist {
|
||||
width: 55%;
|
||||
pre {
|
||||
background-color: rgba(127, 127, 127, 0.1);
|
||||
margin-right: 1em;
|
||||
padding: 1em;
|
||||
padding-left: 2em;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
||||
pre > code {
|
||||
margin-left: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@media (max-width: 760px) {
|
||||
.gist {
|
||||
width: 100%;
|
||||
pre {
|
||||
margin-right: 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -34,10 +43,3 @@ figure iframe {
|
|||
footer p span {
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
pre {
|
||||
background-color: rgba(127, 127, 127, 0.1);
|
||||
padding: 1em;
|
||||
overflow-x: scroll;
|
||||
width: 100%;
|
||||
}
|
||||
|
|
|
@ -41,12 +41,24 @@ module Nodes
|
|||
end
|
||||
|
||||
class Heading1 < Container
|
||||
getter identifier : String
|
||||
|
||||
def initialize(@children : Children, @identifier : String)
|
||||
end
|
||||
end
|
||||
|
||||
class Heading2 < Container
|
||||
getter identifier : String
|
||||
|
||||
def initialize(@children : Children, @identifier : String)
|
||||
end
|
||||
end
|
||||
|
||||
class Heading3 < Container
|
||||
getter identifier : String
|
||||
|
||||
def initialize(@children : Children, @identifier : String)
|
||||
end
|
||||
end
|
||||
|
||||
class ListItem < Container
|
||||
|
@ -135,8 +147,14 @@ module Nodes
|
|||
MAX_WIDTH = 800
|
||||
|
||||
getter src : String
|
||||
getter caption : FigureCaption?
|
||||
|
||||
def initialize(@src : String, @originalWidth : Int32, @originalHeight : Int32)
|
||||
def initialize(
|
||||
@src : String,
|
||||
@originalWidth : Int32,
|
||||
@originalHeight : Int32,
|
||||
@caption : FigureCaption? = nil
|
||||
)
|
||||
end
|
||||
|
||||
def width
|
||||
|
@ -156,7 +174,10 @@ module Nodes
|
|||
end
|
||||
|
||||
def ==(other : EmbeddedContent)
|
||||
other.src == src && other.width == width && other.height == height
|
||||
other.src == src &&
|
||||
other.width == width &&
|
||||
other.height == height &&
|
||||
other.caption == caption
|
||||
end
|
||||
|
||||
def empty?
|
||||
|
|
|
@ -32,6 +32,7 @@ class PostResponse
|
|||
end
|
||||
|
||||
class Paragraph < Base
|
||||
property name : String?
|
||||
property text : String?
|
||||
property type : ParagraphType
|
||||
property markups : Array(Markup)
|
||||
|
@ -40,6 +41,7 @@ class PostResponse
|
|||
property metadata : Metadata?
|
||||
|
||||
def initialize(
|
||||
@name : String,
|
||||
@text : String?,
|
||||
@type : ParagraphType,
|
||||
@markups : Array(Markup),
|
||||
|
|
|
@ -18,19 +18,9 @@ class Faq::IndexPage < MainLayout
|
|||
section do
|
||||
h2 "How-to Automatically Redirect Medium Articles"
|
||||
para do
|
||||
text "If you don't want to manually change the URL every time, you can use an extension to do it for you. "
|
||||
a "This extension", href: "https://einaregilsson.com/redirector/"
|
||||
text " works well across most browsers."
|
||||
end
|
||||
para do
|
||||
text "Once installed download a configuration file by "
|
||||
link "clicking here", to: RedirectionConfig::Index
|
||||
text "."
|
||||
end
|
||||
para do
|
||||
text "Install it by opening the extension preferences, editing redirects, clicking "
|
||||
code "Import"
|
||||
text " and selecting the downloaded file. This will add a new redirection and not overwrite any existing ones. Now visiting any medium.com site (including user.medium.com subdomains) should redirect to Scribe instead!"
|
||||
text "If you don't want to manually change the URL every time, you can use an extension to do it for you. The "
|
||||
a "LibRedirect extention", href: "https://libredirect.github.io/"
|
||||
text " works well across most browsers, and will also redirect to other alternative services."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
module Scribe
|
||||
VERSION = "2022-07-19"
|
||||
VERSION = "2023-05-06"
|
||||
end
|
||||
|
|
1
tasks.cr
1
tasks.cr
|
@ -7,5 +7,6 @@ require "lucky_task"
|
|||
require "./tasks/**"
|
||||
require "./db/migrations/**"
|
||||
require "lucky/tasks/**"
|
||||
require "avram/lucky/tasks"
|
||||
|
||||
LuckyTask::Runner.run
|
||||
|
|
Loading…
Reference in a new issue