diff --git a/.crystal-version b/.crystal-version index bc80560..a8fdfda 100644 --- a/.crystal-version +++ b/.crystal-version @@ -1 +1 @@ -1.5.0 +1.8.1 diff --git a/.tool-versions b/.tool-versions index d933386..83bedd9 100644 --- a/.tool-versions +++ b/.tool-versions @@ -1,2 +1,2 @@ -nodejs 12.14.1 +nodejs 16.18.0 crystal 1.5.0 diff --git a/CHANGELOG b/CHANGELOG index 4362880..a85912e 100644 --- a/CHANGELOG +++ b/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 diff --git a/docs/instances.json b/docs/instances.json index 04d4a40..f96b2cc 100644 --- a/docs/instances.json +++ b/docs/instances.json @@ -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" ] diff --git a/docs/instances.md b/docs/instances.md index 9af930f..ae42fe8 100644 --- a/docs/instances.md +++ b/docs/instances.md @@ -7,7 +7,11 @@ * * * -* (Tor) +* +* (Tor) +* (Tor) +* (Tor) +* [sc.vern.i2p](http://vern3whzyfmjclq6snhlupma6nrmojghwp37tydfgqotj7sc6izq.b32.i2p) (I2P) ## How do I get my instance on this list? diff --git a/shard.lock b/shard.lock index aff3c9c..77c29ce 100644 --- a/shard.lock +++ b/shard.lock @@ -2,31 +2,27 @@ 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 version: 0.1.0+git.commit.46c4c14594057dbcfaf27e7e7c8c164d3f0ce3f1 - carbon: - git: https://github.com/luckyframework/carbon.git - version: 0.2.1 - cry: git: https://github.com/luckyframework/cry.git version: 0.4.3 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 +34,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 +62,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 +86,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 +102,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 diff --git a/shard.yml b/shard.yml index 24bbe56..a5cda11 100644 --- a/shard.yml +++ b/shard.yml @@ -8,18 +8,18 @@ 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 @@ -31,4 +31,4 @@ dependencies: development_dependencies: lucky_flow: github: luckyframework/lucky_flow - version: ~> 0.7.3 + version: ~> 0.9.0 diff --git a/spec/classes/article_id_parser_spec.cr b/spec/classes/article_id_parser_spec.cr index 2baef20..044aebf 100644 --- a/spec/classes/article_id_parser_spec.cr +++ b/spec/classes/article_id_parser_spec.cr @@ -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") diff --git a/spec/classes/embedded_converter_spec.cr b/spec/classes/embedded_converter_spec.cr index 780bc92..1887843 100644 --- a/spec/classes/embedded_converter_spec.cr +++ b/spec/classes/embedded_converter_spec.cr @@ -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, diff --git a/spec/classes/gist_scanner_spec.cr b/spec/classes/gist_scanner_spec.cr index a5648e6..24aaf62 100644 --- a/spec/classes/gist_scanner_spec.cr +++ b/spec/classes/gist_scanner_spec.cr @@ -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, diff --git a/spec/classes/page_converter_spec.cr b/spec/classes/page_converter_spec.cr index ccb07db..6770af2 100644 --- a/spec/classes/page_converter_spec.cr +++ b/spec/classes/page_converter_spec.cr @@ -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": { diff --git a/spec/classes/paragraph_converter_spec.cr b/spec/classes/paragraph_converter_spec.cr index e66b824..b7f7b02 100644 --- a/spec/classes/paragraph_converter_spec.cr +++ b/spec/classes/paragraph_converter_spec.cr @@ -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 diff --git a/spec/components/page_content_spec.cr b/spec/components/page_content_spec.cr index bdcd9ed..5536dd2 100644 --- a/spec/components/page_content_spec.cr +++ b/spec/components/page_content_spec.cr @@ -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 %(

Title!

) + html.should eq %(

Title!

) 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 %(

Title!

) + html.should eq %(

Title!

) 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 %(

In Conclusion...

) + html.should eq %(

In Conclusion...

) 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
+ + + + Caption +
HTML end diff --git a/spec/setup/configure_lucky_flow.cr b/spec/setup/configure_lucky_flow.cr index cc9e935..d651d9d 100644 --- a/spec/setup/configure_lucky_flow.cr +++ b/spec/setup/configure_lucky_flow.cr @@ -4,10 +4,10 @@ LuckyFlow.configure do |settings| settings.stop_retrying_after = 200.milliseconds settings.base_uri = Lucky::RouteHelper.settings.base_uri - + # By default, LuckyFlow is set in "headless" mode (no browser window shown). # Uncomment this to enable running `LuckyFlow` in a Google Chrome window instead. # Be sure to disable for CI. # settings.driver = LuckyFlow::Drivers::Chrome end -Spec.before_each { LuckyFlow::Server::INSTANCE.reset } +LuckyFlow::Spec.setup \ No newline at end of file diff --git a/spec/spec_helper.cr b/spec/spec_helper.cr index 1a9cc7e..f64b64a 100644 --- a/spec/spec_helper.cr +++ b/spec/spec_helper.cr @@ -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/**" diff --git a/src/actions/redirection_config/index.cr b/src/actions/redirection_config/index.cr deleted file mode 100644 index 948e960..0000000 --- a/src/actions/redirection_config/index.cr +++ /dev/null @@ -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?://(?:.*\\\\.)*(? 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 diff --git a/src/classes/article_id_parser.cr b/src/classes/article_id_parser.cr index 8693ea6..639013c 100644 --- a/src/classes/article_id_parser.cr +++ b/src/classes/article_id_parser.cr @@ -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) diff --git a/src/classes/embedded_converter.cr b/src/classes/embedded_converter.cr index 925e54f..12271be 100644 --- a/src/classes/embedded_converter.cr +++ b/src/classes/embedded_converter.cr @@ -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) diff --git a/src/classes/paragraph_converter.cr b/src/classes/paragraph_converter.cr index 00a76f4..267bdb0 100644 --- a/src/classes/paragraph_converter.cr +++ b/src/classes/paragraph_converter.cr @@ -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) diff --git a/src/clients/medium_client.cr b/src/clients/medium_client.cr index 1898497..cc71a1c 100644 --- a/src/clients/medium_client.cr +++ b/src/clients/medium_client.cr @@ -27,6 +27,7 @@ class MediumClient content { bodyModel { paragraphs { + name text type href diff --git a/src/components/page_content.cr b/src/components/page_content.cr index 2849bbc..4f3d094 100644 --- a/src/components/page_content.cr +++ b/src/components/page_content.cr @@ -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) diff --git a/src/css/app.css b/src/css/app.css index dc5a471..9b67ab6 100644 --- a/src/css/app.css +++ b/src/css/app.css @@ -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; } } @@ -118,4 +127,3 @@ code { h1, .meta { text-align: center; } - diff --git a/src/models/nodes.cr b/src/models/nodes.cr index 0ddce68..266ad43 100644 --- a/src/models/nodes.cr +++ b/src/models/nodes.cr @@ -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? diff --git a/src/models/post_response.cr b/src/models/post_response.cr index b295ffd..c707a98 100644 --- a/src/models/post_response.cr +++ b/src/models/post_response.cr @@ -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), diff --git a/src/pages/faq/index_page.cr b/src/pages/faq/index_page.cr index 74f25d4..8eb0681 100644 --- a/src/pages/faq/index_page.cr +++ b/src/pages/faq/index_page.cr @@ -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 diff --git a/src/version.cr b/src/version.cr index 0163f61..9fa3801 100644 --- a/src/version.cr +++ b/src/version.cr @@ -1,3 +1,3 @@ module Scribe - VERSION = "2022-07-19" + VERSION = "2023-05-06" end diff --git a/tasks.cr b/tasks.cr index c192d8a..176c5ae 100644 --- a/tasks.cr +++ b/tasks.cr @@ -7,5 +7,6 @@ require "lucky_task" require "./tasks/**" require "./db/migrations/**" require "lucky/tasks/**" +require "avram/lucky/tasks" LuckyTask::Runner.run