Merge branch 'main' into white-theme

This commit is contained in:
Fabio Manganiello 2023-05-11 14:39:41 +02:00
commit 5ab9c48e2c
27 changed files with 243 additions and 124 deletions

View File

@ -1 +1 @@
1.5.0 1.8.1

View File

@ -1,2 +1,2 @@
nodejs 12.14.1 nodejs 16.18.0
crystal 1.5.0 crystal 1.5.0

View File

@ -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 2022-07-19
* Fix downloadable config file for Redirector extension * Fix downloadable config file for Redirector extension

View File

@ -5,5 +5,7 @@
"https://scribe.bus-hit.me", "https://scribe.bus-hit.me",
"https://scribe.froth.zone", "https://scribe.froth.zone",
"https://scribe.esmailelbob.xyz", "https://scribe.esmailelbob.xyz",
"https://scribe.privacydev.net" "https://scribe.privacydev.net",
"https://scribe.rawbit.ninja",
"https://sc.vern.cc"
] ]

View File

@ -7,7 +7,11 @@
* <https://scribe.froth.zone> * <https://scribe.froth.zone>
* <https://scribe.esmailelbob.xyz> * <https://scribe.esmailelbob.xyz>
* <https://scribe.privacydev.net> * <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? ## How do I get my instance on this list?

View File

@ -2,31 +2,27 @@ version: 2.0
shards: shards:
authentic: authentic:
git: https://github.com/luckyframework/authentic.git git: https://github.com/luckyframework/authentic.git
version: 0.8.2 version: 1.0.0
avram: avram:
git: https://github.com/luckyframework/avram.git git: https://github.com/luckyframework/avram.git
version: 0.23.0 version: 1.0.0
backtracer: backtracer:
git: https://github.com/sija/backtracer.cr.git git: https://github.com/sija/backtracer.cr.git
version: 1.2.1 version: 1.2.2
cadmium_transliterator: cadmium_transliterator:
git: https://github.com/cadmiumcr/transliterator.git git: https://github.com/cadmiumcr/transliterator.git
version: 0.1.0+git.commit.46c4c14594057dbcfaf27e7e7c8c164d3f0ce3f1 version: 0.1.0+git.commit.46c4c14594057dbcfaf27e7e7c8c164d3f0ce3f1
carbon:
git: https://github.com/luckyframework/carbon.git
version: 0.2.1
cry: cry:
git: https://github.com/luckyframework/cry.git git: https://github.com/luckyframework/cry.git
version: 0.4.3 version: 0.4.3
crystar: crystar:
git: https://github.com/naqvis/crystar.git git: https://github.com/naqvis/crystar.git
version: 0.2.0 version: 0.2.0+git.commit.56db8bb9dfbd5ed6d7908353405a5fae632a6561
db: db:
git: https://github.com/crystal-lang/crystal-db.git git: https://github.com/crystal-lang/crystal-db.git
@ -38,15 +34,23 @@ shards:
exception_page: exception_page:
git: https://github.com/crystal-loot/exception_page.git 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: habitat:
git: https://github.com/luckyframework/habitat.git git: https://github.com/luckyframework/habitat.git
version: 0.4.7 version: 0.4.7
html5:
git: https://github.com/naqvis/crystal-html5.git
version: 0.4.0
lucky: lucky:
git: https://github.com/luckyframework/lucky.git git: https://github.com/luckyframework/lucky.git
version: 0.30.1 version: 1.0.0
lucky_cache: lucky_cache:
git: https://github.com/luckyframework/lucky_cache.git git: https://github.com/luckyframework/lucky_cache.git
@ -58,11 +62,11 @@ shards:
lucky_flow: lucky_flow:
git: https://github.com/luckyframework/lucky_flow.git git: https://github.com/luckyframework/lucky_flow.git
version: 0.7.3 version: 0.9.0
lucky_router: lucky_router:
git: https://github.com/luckyframework/lucky_router.git git: https://github.com/luckyframework/lucky_router.git
version: 0.5.1 version: 0.5.2
lucky_task: lucky_task:
git: https://github.com/luckyframework/lucky_task.git git: https://github.com/luckyframework/lucky_task.git
@ -82,7 +86,7 @@ shards:
selenium: selenium:
git: https://github.com/matthewmcgarvey/selenium.cr.git git: https://github.com/matthewmcgarvey/selenium.cr.git
version: 0.9.1 version: 0.10.0
shell-table: shell-table:
git: https://github.com/luckyframework/shell-table.cr.git git: https://github.com/luckyframework/shell-table.cr.git
@ -98,9 +102,17 @@ shards:
webdrivers: webdrivers:
git: https://github.com/matthewmcgarvey/webdrivers.cr.git 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: wordsmith:
git: https://github.com/luckyframework/wordsmith.git 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

View File

@ -8,18 +8,18 @@ targets:
scribe: scribe:
main: src/scribe.cr main: src/scribe.cr
crystal: 1.5.0 crystal: 1.8.1
dependencies: dependencies:
lucky: lucky:
github: luckyframework/lucky github: luckyframework/lucky
version: ~> 0.30.1 version: ~> 1.0.0
avram:
github: luckyframework/avram
version: ~> 1.0.0
authentic: authentic:
github: luckyframework/authentic github: luckyframework/authentic
version: ~> 0.8.2 version: ~> 1.0.0
carbon:
github: luckyframework/carbon
version: ~> 0.2.0
lucky_env: lucky_env:
github: luckyframework/lucky_env github: luckyframework/lucky_env
version: ~> 0.1.4 version: ~> 0.1.4
@ -31,4 +31,4 @@ dependencies:
development_dependencies: development_dependencies:
lucky_flow: lucky_flow:
github: luckyframework/lucky_flow github: luckyframework/lucky_flow
version: ~> 0.7.3 version: ~> 0.9.0

View File

@ -78,6 +78,14 @@ describe ArticleIdParser do
result.should eq(Monads::Just.new("888888abcdef")) result.should eq(Monads::Just.new("888888abcdef"))
end 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 it "returns Nothing if path is a username" do
request = resource_request("/@ba5eba11") request = resource_request("/@ba5eba11")

View File

@ -8,6 +8,7 @@ describe EmbeddedConverter do
store = GistStore.new store = GistStore.new
paragraph = PostResponse::Paragraph.from_json <<-JSON paragraph = PostResponse::Paragraph.from_json <<-JSON
{ {
"name": "ab12",
"text": "", "text": "",
"type": "IFRAME", "type": "IFRAME",
"href": null, "href": null,
@ -36,6 +37,44 @@ describe EmbeddedConverter do
) )
) )
end 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 end
context "when the mediaResource has a blank iframeSrc value" do context "when the mediaResource has a blank iframeSrc value" do
@ -44,6 +83,7 @@ describe EmbeddedConverter do
store = GistStore.new store = GistStore.new
paragraph = PostResponse::Paragraph.from_json <<-JSON paragraph = PostResponse::Paragraph.from_json <<-JSON
{ {
"name": "ab12",
"text": "", "text": "",
"type": "IFRAME", "type": "IFRAME",
"href": null, "href": null,
@ -73,6 +113,7 @@ describe EmbeddedConverter do
store = GistStore.new store = GistStore.new
paragraph = PostResponse::Paragraph.from_json <<-JSON paragraph = PostResponse::Paragraph.from_json <<-JSON
{ {
"name": "ab12",
"text": "", "text": "",
"type": "IFRAME", "type": "IFRAME",
"href": null, "href": null,

View File

@ -12,6 +12,7 @@ describe GistScanner do
) )
paragraphs = [ paragraphs = [
PostResponse::Paragraph.new( PostResponse::Paragraph.new(
name: "ab12",
text: "Check out this gist:", text: "Check out this gist:",
type: PostResponse::ParagraphType::P, type: PostResponse::ParagraphType::P,
markups: [] of PostResponse::Markup, markups: [] of PostResponse::Markup,
@ -20,6 +21,7 @@ describe GistScanner do
metadata: nil metadata: nil
), ),
PostResponse::Paragraph.new( PostResponse::Paragraph.new(
name: "ab13",
text: "", text: "",
type: PostResponse::ParagraphType::IFRAME, type: PostResponse::ParagraphType::IFRAME,
markups: [] of PostResponse::Markup, markups: [] of PostResponse::Markup,
@ -45,6 +47,7 @@ describe GistScanner do
) )
paragraphs = [ paragraphs = [
PostResponse::Paragraph.new( PostResponse::Paragraph.new(
name: "ab12",
text: "", text: "",
type: PostResponse::ParagraphType::IFRAME, type: PostResponse::ParagraphType::IFRAME,
markups: [] of PostResponse::Markup, markups: [] of PostResponse::Markup,
@ -78,6 +81,7 @@ describe GistScanner do
) )
paragraphs = [ paragraphs = [
PostResponse::Paragraph.new( PostResponse::Paragraph.new(
name: "ab12",
text: "", text: "",
type: PostResponse::ParagraphType::IFRAME, type: PostResponse::ParagraphType::IFRAME,
markups: [] of PostResponse::Markup, markups: [] of PostResponse::Markup,
@ -86,6 +90,7 @@ describe GistScanner do
metadata: nil metadata: nil
), ),
PostResponse::Paragraph.new( PostResponse::Paragraph.new(
name: "ab13",
text: "", text: "",
type: PostResponse::ParagraphType::IFRAME, type: PostResponse::ParagraphType::IFRAME,
markups: [] of PostResponse::Markup, markups: [] of PostResponse::Markup,

View File

@ -8,6 +8,7 @@ describe PageConverter do
paragraph_json = <<-JSON paragraph_json = <<-JSON
[ [
{ {
"name": "ab12",
"text": "#{title}", "text": "#{title}",
"type": "H3", "type": "H3",
"markups": [], "markups": [],
@ -28,6 +29,7 @@ describe PageConverter do
it "sets the author" do it "sets the author" do
post_json = <<-JSON post_json = <<-JSON
{ {
"name": "ab12",
"title": "This is a story", "title": "This is a story",
"createdAt": 0, "createdAt": 0,
"creator": { "creator": {
@ -52,6 +54,7 @@ describe PageConverter do
it "sets the publish date/time" do it "sets the publish date/time" do
post_json = <<-JSON post_json = <<-JSON
{ {
"name": "ab12",
"title": "This is a story", "title": "This is a story",
"createdAt": 1000, "createdAt": 1000,
"creator": { "creator": {
@ -77,6 +80,7 @@ describe PageConverter do
paragraph_json = <<-JSON paragraph_json = <<-JSON
[ [
{ {
"name": "ab12",
"text": "#{title}", "text": "#{title}",
"type": "H3", "type": "H3",
"markups": [], "markups": [],
@ -85,6 +89,7 @@ describe PageConverter do
"metadata": null "metadata": null
}, },
{ {
"name": "ab12",
"text": "Content", "text": "Content",
"type": "P", "type": "P",
"markups": [], "markups": [],
@ -117,6 +122,7 @@ def default_post_json(
) )
<<-JSON <<-JSON
{ {
"name": "ab12",
"title": "#{title}", "title": "#{title}",
"createdAt": 1628974309758, "createdAt": 1628974309758,
"creator": { "creator": {

View File

@ -8,6 +8,7 @@ describe ParagraphConverter do
paragraphs = Array(PostResponse::Paragraph).from_json <<-JSON paragraphs = Array(PostResponse::Paragraph).from_json <<-JSON
[ [
{ {
"name": "ab12",
"text": "Title", "text": "Title",
"type": "H3", "type": "H3",
"markups": [], "markups": [],
@ -17,7 +18,7 @@ describe ParagraphConverter do
} }
] ]
JSON 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) result = ParagraphConverter.new.convert(paragraphs, gist_store)
@ -29,6 +30,7 @@ describe ParagraphConverter do
paragraphs = Array(PostResponse::Paragraph).from_json <<-JSON paragraphs = Array(PostResponse::Paragraph).from_json <<-JSON
[ [
{ {
"name": "ab12",
"text": "inline code", "text": "inline code",
"type": "P", "type": "P",
"markups": [ "markups": [
@ -66,6 +68,7 @@ describe ParagraphConverter do
paragraphs = Array(PostResponse::Paragraph).from_json <<-JSON paragraphs = Array(PostResponse::Paragraph).from_json <<-JSON
[ [
{ {
"name": "ab12",
"text": "One", "text": "One",
"type": "ULI", "type": "ULI",
"markups": [], "markups": [],
@ -74,6 +77,7 @@ describe ParagraphConverter do
"metadata": null "metadata": null
}, },
{ {
"name": "ab13",
"text": "Two", "text": "Two",
"type": "ULI", "type": "ULI",
"markups": [], "markups": [],
@ -82,6 +86,7 @@ describe ParagraphConverter do
"metadata": null "metadata": null
}, },
{ {
"name": "ab14",
"text": "Not a list item", "text": "Not a list item",
"type": "P", "type": "P",
"markups": [], "markups": [],
@ -109,6 +114,7 @@ describe ParagraphConverter do
paragraphs = Array(PostResponse::Paragraph).from_json <<-JSON paragraphs = Array(PostResponse::Paragraph).from_json <<-JSON
[ [
{ {
"name": "ab12",
"text": "One", "text": "One",
"type": "OLI", "type": "OLI",
"markups": [], "markups": [],
@ -117,6 +123,7 @@ describe ParagraphConverter do
"metadata": null "metadata": null
}, },
{ {
"name": "ab13",
"text": "Two", "text": "Two",
"type": "OLI", "type": "OLI",
"markups": [], "markups": [],
@ -125,6 +132,7 @@ describe ParagraphConverter do
"metadata": null "metadata": null
}, },
{ {
"name": "ab14",
"text": "Not a list item", "text": "Not a list item",
"type": "P", "type": "P",
"markups": [], "markups": [],
@ -151,6 +159,7 @@ describe ParagraphConverter do
gist_store = GistStore.new gist_store = GistStore.new
paragraph = PostResponse::Paragraph.from_json <<-JSON paragraph = PostResponse::Paragraph.from_json <<-JSON
{ {
"name": "ab12",
"text": "Image by someuser", "text": "Image by someuser",
"type": "IMG", "type": "IMG",
"markups": [ "markups": [
@ -197,6 +206,7 @@ describe ParagraphConverter do
paragraphs = Array(PostResponse::Paragraph).from_json <<-JSON paragraphs = Array(PostResponse::Paragraph).from_json <<-JSON
[ [
{ {
"name": "ab12",
"text": "text", "text": "text",
"type": "H2", "type": "H2",
"markups": [], "markups": [],
@ -205,6 +215,7 @@ describe ParagraphConverter do
"metadata": null "metadata": null
}, },
{ {
"name": "ab13",
"text": "text", "text": "text",
"type": "H3", "type": "H3",
"markups": [], "markups": [],
@ -213,6 +224,7 @@ describe ParagraphConverter do
"metadata": null "metadata": null
}, },
{ {
"name": "ab14",
"text": "text", "text": "text",
"type": "H4", "type": "H4",
"markups": [], "markups": [],
@ -221,6 +233,7 @@ describe ParagraphConverter do
"metadata": null "metadata": null
}, },
{ {
"name": "ab15",
"text": "text", "text": "text",
"type": "P", "type": "P",
"markups": [], "markups": [],
@ -229,6 +242,7 @@ describe ParagraphConverter do
"metadata": null "metadata": null
}, },
{ {
"name": "ab16",
"text": "text", "text": "text",
"type": "PRE", "type": "PRE",
"markups": [], "markups": [],
@ -237,6 +251,7 @@ describe ParagraphConverter do
"metadata": null "metadata": null
}, },
{ {
"name": "ab17",
"text": "text", "text": "text",
"type": "BQ", "type": "BQ",
"markups": [], "markups": [],
@ -245,6 +260,7 @@ describe ParagraphConverter do
"metadata": null "metadata": null
}, },
{ {
"name": "ab18",
"text": "text", "text": "text",
"type": "PQ", "type": "PQ",
"markups": [], "markups": [],
@ -253,6 +269,7 @@ describe ParagraphConverter do
"metadata": null "metadata": null
}, },
{ {
"name": "ab19",
"text": "text", "text": "text",
"type": "ULI", "type": "ULI",
"markups": [], "markups": [],
@ -261,6 +278,7 @@ describe ParagraphConverter do
"metadata": null "metadata": null
}, },
{ {
"name": "ab20",
"text": "text", "text": "text",
"type": "OLI", "type": "OLI",
"markups": [], "markups": [],
@ -269,6 +287,7 @@ describe ParagraphConverter do
"metadata": null "metadata": null
}, },
{ {
"name": "ab21",
"text": "text", "text": "text",
"type": "IMG", "type": "IMG",
"markups": [], "markups": [],
@ -281,6 +300,7 @@ describe ParagraphConverter do
} }
}, },
{ {
"name": "ab22",
"text": "", "text": "",
"type": "IFRAME", "type": "IFRAME",
"markups": [], "markups": [],
@ -296,6 +316,7 @@ describe ParagraphConverter do
"metadata": null "metadata": null
}, },
{ {
"name": "ab23",
"text": "Mixtape", "text": "Mixtape",
"type": "MIXTAPE_EMBED", "type": "MIXTAPE_EMBED",
"href": null, "href": null,
@ -317,9 +338,9 @@ describe ParagraphConverter do
] ]
JSON JSON
expected = [ expected = [
Heading1.new([Text.new("text")] of Child), Heading1.new([Text.new("text")] of Child, identifier: "ab12"),
Heading2.new([Text.new("text")] of Child), Heading2.new([Text.new("text")] of Child, identifier: "ab13"),
Heading3.new([Text.new("text")] of Child), Heading3.new([Text.new("text")] of Child, identifier: "ab14"),
Paragraph.new([Text.new("text")] of Child), Paragraph.new([Text.new("text")] of Child),
Preformatted.new([Text.new("text")] of Child), Preformatted.new([Text.new("text")] of Child),
BlockQuote.new([Text.new("text")] of Child), # BQ BlockQuote.new([Text.new("text")] of Child), # BQ

View File

@ -184,13 +184,13 @@ describe PageContent do
nodes: [ nodes: [
Heading1.new(children: [ Heading1.new(children: [
Text.new(content: "Title!"), Text.new(content: "Title!"),
] of Child), ] of Child, identifier: "ab12"),
] of Child ] of Child
) )
html = PageContent.new(page: page).render_to_string html = PageContent.new(page: page).render_to_string
html.should eq %(<h1>Title!</h1>) html.should eq %(<h1 id="ab12">Title!</h1>)
end end
it "renders an H3" do it "renders an H3" do
@ -201,13 +201,13 @@ describe PageContent do
nodes: [ nodes: [
Heading2.new(children: [ Heading2.new(children: [
Text.new(content: "Title!"), Text.new(content: "Title!"),
] of Child), ] of Child, identifier: "ab12"),
] of Child ] of Child
) )
html = PageContent.new(page: page).render_to_string html = PageContent.new(page: page).render_to_string
html.should eq %(<h2>Title!</h2>) html.should eq %(<h2 id="ab12">Title!</h2>)
end end
it "renders an H4" do it "renders an H4" do
@ -218,13 +218,13 @@ describe PageContent do
nodes: [ nodes: [
Heading3.new(children: [ Heading3.new(children: [
Text.new(content: "In Conclusion..."), Text.new(content: "In Conclusion..."),
] of Child), ] of Child, identifier: "ab12"),
] of Child ] of Child
) )
html = PageContent.new(page: page).render_to_string 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 end
it "renders an image" do it "renders an image" do
@ -249,6 +249,7 @@ describe PageContent do
end end
it "renders embedded content" do it "renders embedded content" do
caption_children = [Text.new("Caption")] of Child
page = Page.new( page = Page.new(
title: "Title", title: "Title",
author: user_anchor_factory, author: user_anchor_factory,
@ -258,6 +259,7 @@ describe PageContent do
src: "https://example.com", src: "https://example.com",
originalWidth: 1000, originalWidth: 1000,
originalHeight: 600, originalHeight: 600,
caption: FigureCaption.new(children: caption_children)
), ),
] of Child ] of Child
) )
@ -268,6 +270,11 @@ describe PageContent do
<figure> <figure>
<iframe src="https://example.com" width="800" height="480" frameborder="0" allowfullscreen="true"> <iframe src="https://example.com" width="800" height="480" frameborder="0" allowfullscreen="true">
</iframe> </iframe>
<label class="margin-toggle" for="#{caption_children.hash}">&#9997;&#xFE0E;</label>
<input class="margin-toggle" type="checkbox" id="#{caption_children.hash}">
<span class="marginnote">
Caption
</span>
</figure> </figure>
HTML HTML
end end

View File

@ -10,4 +10,4 @@ LuckyFlow.configure do |settings|
# Be sure to disable for CI. # Be sure to disable for CI.
# settings.driver = LuckyFlow::Drivers::Chrome # settings.driver = LuckyFlow::Drivers::Chrome
end end
Spec.before_each { LuckyFlow::Server::INSTANCE.reset } LuckyFlow::Spec.setup

View File

@ -2,6 +2,8 @@ ENV["LUCKY_ENV"] = "test"
ENV["DEV_PORT"] = "5001" ENV["DEV_PORT"] = "5001"
require "spec" require "spec"
require "lucky_flow" require "lucky_flow"
require "lucky_flow/ext/lucky"
require "lucky_flow/ext/avram"
require "../src/app" require "../src/app"
require "./support/flows/base_flow" require "./support/flows/base_flow"
require "./support/**" require "./support/**"

View File

@ -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

View File

@ -1,7 +1,7 @@
class ArticleIdParser class ArticleIdParser
include Monads include Monads
ID_REGEX = /[\/\-]([0-9a-f]+)$/i ID_REGEX = /[\/\-]([0-9a-f]+)\/?$/i
def self.parse(request : HTTP::Request) def self.parse(request : HTTP::Request)
new.parse(request) new.parse(request)
@ -10,7 +10,7 @@ class ArticleIdParser
def parse(request : HTTP::Request) : Maybe def parse(request : HTTP::Request) : Maybe
from_params = post_id_from_params(request.query_params) from_params = post_id_from_params(request.query_params)
from_path = post_id_from_path(request.path) from_path = post_id_from_path(request.path)
from_path.or(from_params) from_params.or(from_path)
end end
private def post_id_from_path(request_path : String) private def post_id_from_path(request_path : String)

View File

@ -34,11 +34,19 @@ class EmbeddedConverter
EmbeddedContent.new( EmbeddedContent.new(
src: media.iframeSrc, src: media.iframeSrc,
originalWidth: media.iframeWidth, originalWidth: media.iframeWidth,
originalHeight: media.iframeHeight originalHeight: media.iframeHeight,
caption: caption
) )
end end
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 private def custom_embed(media : PostResponse::MediaResource) : Embedded
if media.href.starts_with?(GIST_HOST_AND_SCHEME) if media.href.starts_with?(GIST_HOST_AND_SCHEME)
GithubGist.new(href: media.href, gist_store: gist_store) GithubGist.new(href: media.href, gist_store: gist_store)

View File

@ -16,15 +16,15 @@ class ParagraphConverter
when PostResponse::ParagraphType::H2 when PostResponse::ParagraphType::H2
paragraph = paragraphs.shift paragraph = paragraphs.shift
children = MarkupConverter.convert(paragraph.text, paragraph.markups) children = MarkupConverter.convert(paragraph.text, paragraph.markups)
node = Heading1.new(children: children) node = Heading1.new(children: children, identifier: paragraph.name || "")
when PostResponse::ParagraphType::H3 when PostResponse::ParagraphType::H3
paragraph = paragraphs.shift paragraph = paragraphs.shift
children = MarkupConverter.convert(paragraph.text, paragraph.markups) children = MarkupConverter.convert(paragraph.text, paragraph.markups)
node = Heading2.new(children: children) node = Heading2.new(children: children, identifier: paragraph.name || "")
when PostResponse::ParagraphType::H4 when PostResponse::ParagraphType::H4
paragraph = paragraphs.shift paragraph = paragraphs.shift
children = MarkupConverter.convert(paragraph.text, paragraph.markups) children = MarkupConverter.convert(paragraph.text, paragraph.markups)
node = Heading3.new(children: children) node = Heading3.new(children: children, identifier: paragraph.name || "")
when PostResponse::ParagraphType::IFRAME when PostResponse::ParagraphType::IFRAME
paragraph = paragraphs.shift paragraph = paragraphs.shift
node = EmbeddedConverter.convert(paragraph, gist_store) node = EmbeddedConverter.convert(paragraph, gist_store)

View File

@ -27,6 +27,7 @@ class MediumClient
content { content {
bodyModel { bodyModel {
paragraphs { paragraphs {
name
text text
type type
href href

View File

@ -40,6 +40,9 @@ class PageContent < BaseComponent
frameborder: "0", frameborder: "0",
allowfullscreen: true, allowfullscreen: true,
) )
if caption = child.caption
render_child(caption)
end
end end
end end
@ -93,15 +96,15 @@ class PageContent < BaseComponent
end end
def render_child(node : Heading1) def render_child(node : Heading1)
h1 { render_children(node.children) } h1(id: node.identifier) { render_children(node.children) }
end end
def render_child(node : Heading2) def render_child(node : Heading2)
h2 { render_children(node.children) } h2(id: node.identifier) { render_children(node.children) }
end end
def render_child(node : Heading3) def render_child(node : Heading3)
h3 { render_children(node.children) } h3(id: node.identifier) { render_children(node.children) }
end end
def render_child(child : Image) def render_child(child : Image)

View File

@ -7,13 +7,22 @@ p.meta {
line-height: 1; line-height: 1;
} }
.gist { pre {
width: 55%; 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) { @media (max-width: 760px) {
.gist { pre {
width: 100%; margin-right: 0;
} }
} }
@ -118,4 +127,3 @@ code {
h1, .meta { h1, .meta {
text-align: center; text-align: center;
} }

View File

@ -41,12 +41,24 @@ module Nodes
end end
class Heading1 < Container class Heading1 < Container
getter identifier : String
def initialize(@children : Children, @identifier : String)
end
end end
class Heading2 < Container class Heading2 < Container
getter identifier : String
def initialize(@children : Children, @identifier : String)
end
end end
class Heading3 < Container class Heading3 < Container
getter identifier : String
def initialize(@children : Children, @identifier : String)
end
end end
class ListItem < Container class ListItem < Container
@ -135,8 +147,14 @@ module Nodes
MAX_WIDTH = 800 MAX_WIDTH = 800
getter src : String 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 end
def width def width
@ -156,7 +174,10 @@ module Nodes
end end
def ==(other : EmbeddedContent) 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 end
def empty? def empty?

View File

@ -32,6 +32,7 @@ class PostResponse
end end
class Paragraph < Base class Paragraph < Base
property name : String?
property text : String? property text : String?
property type : ParagraphType property type : ParagraphType
property markups : Array(Markup) property markups : Array(Markup)
@ -40,6 +41,7 @@ class PostResponse
property metadata : Metadata? property metadata : Metadata?
def initialize( def initialize(
@name : String,
@text : String?, @text : String?,
@type : ParagraphType, @type : ParagraphType,
@markups : Array(Markup), @markups : Array(Markup),

View File

@ -18,19 +18,9 @@ class Faq::IndexPage < MainLayout
section do section do
h2 "How-to Automatically Redirect Medium Articles" h2 "How-to Automatically Redirect Medium Articles"
para do 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. " 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 "This extension", href: "https://einaregilsson.com/redirector/" a "LibRedirect extention", href: "https://libredirect.github.io/"
text " works well across most browsers." text " works well across most browsers, and will also redirect to other alternative services."
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!"
end end
end end
end end

View File

@ -1,3 +1,3 @@
module Scribe module Scribe
VERSION = "2022-07-19" VERSION = "2023-05-06"
end end

View File

@ -7,5 +7,6 @@ require "lucky_task"
require "./tasks/**" require "./tasks/**"
require "./db/migrations/**" require "./db/migrations/**"
require "lucky/tasks/**" require "lucky/tasks/**"
require "avram/lucky/tasks"
LuckyTask::Runner.run LuckyTask::Runner.run