pardall_markdown

网站数据 2025-08-12

PardallmarkDown

目录

  • 介绍
    • 寻找贡献者配x
  • 视频演示和教程
  • 特征
    • 用例
  • 在长生不老药应用程序中使用
    • 凤凰应用程序的使用
  • API
    • 型号
  • SLUG:帖子,页面,类别和树的唯一标识符
  • 降价文件元数据和属性
    • 元数据解析器
      • 长辈地图
      • 乔普林注意
      • 自定义解析器
  • 使用_index.md文件的分类配置
  • 帖子和页面
  • 内容层次结构,分类学,类别和部分
  • 树木
    • 分类树
    • 内容树
    • 导航后
    • 目录
  • 背压
  • 常问问题
    • 如何将其与Phoenix和Phoenix Liveview集成?
    • PardallmarkDown vs静态网站发电机(Hugo,Docusaurs等)
    • PardallmarkDown与NimblePublisher
    • 如何将内容与PardallmarkDown同步?
    • 如何在您的计算机上本地编写Markdown并立即将其发布到PardallmarkDown应用程序或网站上?
    • 它需要数据库吗?
    • 为什么使用ecto?
  • 谁在使用?
  • 路线图
  • 赞助商和捐赠
  • 贡献者
  • 版权许可
    • 其他通知

介绍

PardallmarkDown是用长生不老药编写的反应性出版框架和引擎。即时网站和文档网站。

与静态网站生成器(例如Hugo,Docusaurs等)相比,在PardallmarkDown中,您无需每次编写或修改新内容时重新编译和重新发布应用程序。该应用程序可以在生产中无限期地运行,而它可以观看内容文件夹以进行更改,并且新内容可通过应用程序重复进行消费

内容文件夹可以是应用程序服务器中的任何路径,也可以是GIT存储库,因为PardallmarkDown支持自动从GIT存储库中提取更改

寻找贡献者配x

我正在寻找一个贡献者,以制作演示项目的更精细的HTML和CSS模板。在此处检查当前的演示,包括其内部部分。这是博客 +文档 + Wiki网站的混合。

像Docusaurus模板之类的东西非常适合该项目。 Docusaurus主题是MIT许可的,因此甚至可以将其提取为PardallmarkDown使用。但是CSS是我不太喜欢的事情,因此,这就是为什么我寻求帮助(即使您可以将Docusaurus主题提取为静态HTML模板,也很棒)。

当前的演示项目和HTML模板具有自己的存储库。因此,您甚至不需要了解Elixir,只有HTML和CSS。任何帮助将不胜感激!

视频演示和教程

请参阅行动中的PardallmarkDown,并通过遵循此视频学习如何使用它:

特征

  • 基于文件系统,并具有降价和静态文件支持。
    • 降价文件被解析为HTML。
  • FileWatcher ,可以检测新内容和修改现有内容,然后自动重新分配并重建内容
    • 无需重新编译和重新启动应用程序或网站,新内容可立即可用(取决于以下设置的间隔设置:recheck_pending_file_events_interval ,请参见下文)。
    • 考虑到Phoenix Liveview和Phoenix频道创建:创建或修改帖子或全新的帖子,它们立即在网站上发布。查看演示存储库。
  • 支持应用程序之外的内容文件夹,这样,可以立即从源位置(例如,您的计算机)同步新内容文件,然后由FileWatcher拾取。
  • RepositoryWatcher可检测从GIT存储库(可选)的变化,并自动将内容拉到PardallmarkDown的内容文件夹中。
    • 由于FileWatcher观看了内容文件夹,因此PardallmarkDown随后每次都会从GIT存储库中重建整个内容。
  • 自动创建来自Markdown标头的目录
  • 无限内容层次结构(类别和子类别,部分和子段)。
    • 不同集合的自定义层次结构和发布集。例如,一个带有文档博客新闻Wiki的网站又具有自己的子层次结构。
    • 每个层次结构集的自定义排序规则。例如,文档层次结构中的帖子可以通过优先级对文档进行排序,按日期按日期和Wiki帖子按标题进行排序。
  • 自动创建分类树和内容曲折
    • 还创建了单独的内容树。例如,文档层次结构的内容树,其中包含指向所有子层次结构和帖子的链接。
  • 自动创建后导航链接(下一个和上一个帖子)。
  • 将可自由嵌入的元数据自由嵌入到帖子中,可以选择用于元数据(Elixir Map,Joplin Notes,Yaml FrontMatter等)的解析器(或自定义)。
  • 层次结构档案列表
  • 所有内容和索引都保存在内存缓存(Elixir的ETS)中。

用例

  • 博客
  • 文档网站
  • 维基
  • 常见问题解答
  • 实际上有什么网站吗?即使是电子商务网站,您也可以在其中使用PardallmarkDown解析的内容作为产品页面等等。
  • 有需要内容的应用程序吗?

在长生不老药应用程序中使用

将依赖项和应用程序添加到您的mix.exs中。exs:

pardall_markdown, "~> 0.4"} ...] end def application do [extra_applications: [: pardall_markdown , ...], ...] end">
 defp deps do
  [ { : pardall_markdown , "~> 0.4" } ... ]
end

def application do
  [ extra_applications: [ : pardall_markdown , ... ] , ... ]
end

将配置(需要所有密钥)添加到config.exs或在任何环境中的特定配置文件:

pardall_markdown, PardallMarkdown.Content, # Where all of the uncompiled assets and content will live on # (the Markdown files and any static asset). In this path # you will create and update content. # # This can be any relative or absolute path, # including outside of the application, examples: # "/home/documents/content", "./content", "../path/to/content" root_path: "/home/documents/content", # The path that contains static assets, # those files won't be parsed. static_assets_path: "/home/documents/content/static", # ETS tables names cache_name: :content_cache, index_cache_name: :content_index_cache, # How often in ms the FileWatcher should check for # new or modified content in the `root_path:`? recheck_pending_file_events_interval: 5_000, # Git repository to watch and automatically fetch content from, # leave "" or nil to not get content from a repository. # # The repository is cloned into `:remote_repository_local_path`. # # By not setting a repository URL, you have to fill and modify # content into the `:root_path` by other means (even manually). remote_repository_url: "", # Where to clone the repository? Must be the same as :root_path or the parent path of :root_path. # Example 1 - parent path of :root_path: # - `root_path`: "/content/markdown" # - `remote_repository_local_path`: "/content" # # Example 2 - the same as :root_path: # - `root_path`: "/content" # - `remote_repository_local_path`: "/content" remote_repository_local_path: "/home/documents/content", # How often in ms the RepositoryWatcher should pool # the `:remote_repository_url` Git repository and `fetch` changes? # # If `:remote_repository_url` is provided, # `:recheck_pending_remote_events_interval` must # be BIGGER than `:recheck_pending_file_events_interval` recheck_pending_remote_events_interval: 15_000, # Should the main content tree contain a link to the Home/Root page ("/")? content_tree_display_home: false, # Should internal links be converted to `Phoenix.LiveView` links? # If you are using PardallMarkdown with a `Phoenix.LiveView` application, you # definitely want this as `true`. convert_internal_links_to_live_links: true, # Markdown files can contain a top section with metadata/attributes related # to the file. Is the metadata required? is_markdown_metadata_required: true, # Are posts set as draft (unpublished) by default? If true, posts will appear # in the content trees only when they have the attribute `published: true` # (which sets `Post.is_published == true`). Draft posts can be retrieved # only by calling their slug directly with `Repository.get_by_slug/1` is_content_draft_by_default: true, # Which parser to use to parse a Markdown file's metadata? # See the documentation section *Markdown file metadata and attributes* metadata_parser: PardallMarkdown.MetadataParser.ElixirMap, # Callback to be called every time the content and the indexes are rebuilt. # # For example, you can put a reference to a function that calls Endpoint.broadcast!: # notify_content_reloaded: &MyPhoenixApp.content_reloaded/0 # # Definition: # def content_reloaded do # Application.ensure_all_started(:my_phoenix_app) # Recommended # MyPhoenixApp.Endpoint.broadcast!(" pardall_markdown _web", "content_reloaded", :all) # end notify_content_reloaded: &MyApp.content_reloaded/0">
 config : pardall_markdown , PardallMarkdown.Content ,
  # Where all of the uncompiled assets and content will live on
  # (the Markdown files and any static asset). In this path
  # you will create and update content.
  #
  # This can be any relative or absolute path,
  # including outside of the application, examples:
  # "/home/documents/content", "./content", "../path/to/content"
  root_path: "/home/documents/content" ,

  # The path that contains static assets,
  # those files won't be parsed.
  static_assets_path: "/home/documents/content/static" ,

  # ETS tables names
  cache_name: :content_cache ,  
  index_cache_name: :content_index_cache ,

  # How often in ms the FileWatcher should check for
  # new or modified content in the `root_path:`?
  recheck_pending_file_events_interval: 5_000 ,

  # Git repository to watch and automatically fetch content from,
  # leave "" or nil to not get content from a repository.
  #
  # The repository is cloned into `:remote_repository_local_path`.
  #
  # By not setting a repository URL, you have to fill and modify
  # content into the `:root_path` by other means (even manually).
  remote_repository_url: "" ,

  # Where to clone the repository? Must be the same as :root_path or the parent path of :root_path.
  # Example 1 - parent path of :root_path:
  # - `root_path`: "/content/markdown"
  # - `remote_repository_local_path`: "/content"
  #
  # Example 2 - the same as :root_path:
  # - `root_path`: "/content"
  # - `remote_repository_local_path`: "/content"
  remote_repository_local_path: "/home/documents/content" ,

  # How often in ms the RepositoryWatcher should pool
  # the `:remote_repository_url` Git repository and `fetch` changes?
  #
  # If `:remote_repository_url` is provided,
  # `:recheck_pending_remote_events_interval` must
  # be BIGGER than `:recheck_pending_file_events_interval`
  recheck_pending_remote_events_interval: 15_000 ,

  # Should the main content tree contain a link to the Home/Root page ("/")?
  content_tree_display_home: false ,

  # Should internal  links be converted to `Phoenix.LiveView` links?
  # If you are using PardallMarkdown with a `Phoenix.LiveView` application, you
  # definitely want this as `true`.
  convert_internal_links_to_live_links: true ,

  # Markdown files can contain a top section with metadata/attributes related
  # to the file. Is the metadata required?
  is_markdown_metadata_required: true ,

  # Are posts set as draft (unpublished) by default? If true, posts will appear
  # in the content trees only when they have the attribute `published: true`
  # (which sets `Post.is_published == true`). Draft posts can be retrieved
  # only by calling their slug directly with `Repository.get_by_slug/1`
  is_content_draft_by_default: true ,

  # Which parser to use to parse a Markdown file's metadata?
  # See the documentation section *Markdown file metadata and attributes*
  metadata_parser: PardallMarkdown.MetadataParser.ElixirMap ,

  # Callback to be called every time the content and the indexes are rebuilt.
  #
  # For example, you can put a reference to a function that calls Endpoint.broadcast!:
  # notify_content_reloaded: &MyPhoenixApp.content_reloaded/0
  #
  # Definition:
  # def content_reloaded do
  #   Application.ensure_all_started(:my_phoenix_app) # Recommended
  #   MyPhoenixApp.Endpoint.broadcast!(" pardall_markdown _web", "content_reloaded", :all)
  # end
  notify_content_reloaded: & MyApp . content_reloaded / 0

凤凰应用程序的使用

除了主要所需:static_assets_path之外,如果要使用静态文件,请在Phoenix.Endpoint配置中添加Plug.Static

 plug Plug.Static ,
    at: "/static/" , # if the static assets path ends at "/static", i.e. /path/to/content/static
    from: Content.Utils . static_assets_path ( ) ,
    gzip: true

检查演示申请中是否有完整的Phoenix应用程序样本,包括样本内容。或观看PardallmarkDown Phoenix Liveview教程视频。

API

使用PardallMarkdown.Repository检索内容。检查文档中的详细信息和说明。

 def get_all_posts ( type \ :all )
def get_all_links ( type \ :all )
def get_taxonomy_tree ( )
def get_content_tree ( slug \ "/" )
def get_all_published ( )
def get_by_slug ( slug )
def get_by_slug! ( slug )

型号

API返回的内容是类型:

  • PardallMarkdown.Content.Post (文档)
  • PardallMarkdown.Content.Link (文档)
  • PardallMarkdown.Content.AnchorLink (文档)

SLUG:帖子,页面,类别和树的唯一标识符

每个内容都有一个唯一的标识符,这仅仅是内容URL slug,例如: "/blog""/docs/getting-started/how-to-install始终具有预先固定的斜线,但从来都不是trail slash。

SLUG用于使用PardallMarkdown.Repository重新函数:各个内容,树木和档案。 SLUG也是如何在缓存中识别内容的方式。

SLUG是从文件路径自动生成的。例如,一个名为: "/blog/news/Top news of_today.md"的降价文件将具有slug: "/blog/news/top-news-of-today"

降价文件元数据和属性

Markdown文件可能包括顶部的元数据 /属性 /配置,其目的与前提相同。

元数据必须遵循所选PardallMarkdown.MetadataParser的模式和规格。解析器用配置密钥定义:metadata_parser

默认情况下,需要元数据,但是可以通过配置使其可选:is_markdown_metadata_required

可用以下配置属性(所有可选):

  • title :帖子标题。如果未提供,将从邮政slug生成标题。
  • date :帖子,字符串,ISO格式的日期或日期时间。如果未提供,则文件修改日期将被视为发布日期。
  • published :没有published: true集将被视为草稿。当配置:is_content_draft_by_default设置为false时,默认值可以倒置,这样,帖子将始终视为发布,除非它们包含: published: false
  • summary :帖子说明或简短内容。如果未提供summary ,将从帖子的内容/正文中产生摘要。
  • position :如果帖子最高分类法具有:sort_by规则设置为:position ,则这是用于对帖子进行排序的值(请参见下文)。
  • slug :覆盖邮局。如上所述,默认情况下,slug是从文件名生成的,是帖子的主要唯一标识符。
    • 如果您用此属性覆盖sl,请确保将完整的路径放在斜线上,例如: slug: "/my/custom/slug"
    • 当与此属性覆盖Slug时,您有责任放置不冲突的sl。
  • 任何其他额外的属性将保存到邮政的PardallMarkdown.Content.Post.metadata字段中。

元数据解析器

长辈地图

元数据是用长生不老药图定义的,由---隔开。属性是MAP键作为原子。

例子:

 % {
    title: "PardallMarkdown public release" ,
    date: "2021-09-11" , # or "2021-09-11T14:40:00Z"
    published: true ,
    summary: "This post announces the launch of the project" ,
    position: 0 ,
    my_custom_data: :used_by_my_custom_application
}
---
Content goes here

如果要使用自动值,则映射可以为空(或者可以在is_markdown_metadata_required: false ):

 % {
}
---
Content goes here 

乔普林注意

从Joplin注释导出的Markdown帖子始终包含顶部的帖子标题,由空白行隔开,示例:

 Post title

... content goes here

Parser PardallMarkdown.MetadataParser.JoplinNote可以使用Joplin Notes。请注意,即使在乔普林(Joplin)内部,您也可以将元数据添加到Joplin注释中。元数据格式必须在选定的解析器中,然后您只需在joplin本身的注释顶部添加它,例如:

注意标题:“帖子标题”

 %{
  position: 0
}
---
Content goes here.

乔普林将其导出为:

 Post title

%{
  position: 0
}
---
Content goes here.

您可以将任何元数据解析器用于Joplin Notes(默认情况下,使用PardallMarkdown.MetadataParser.ElixirMap的Elixir Maps)使用配置:

pardall_markdown, PardallMarkdown.MetadataParser.JoplinNote, metadata_parser_after_title: PardallMarkdown.MetadataParser.ElixirMap">
 config : pardall_markdown , PardallMarkdown.MetadataParser.JoplinNote,
  metadata_parser_after_title: PardallMarkdown.MetadataParser.ElixirMap

自定义解析器

要实现解析器,请检查行为PardallMarkdown.MetadataParser和现有包含的解析器(in /lib/ pardall_markdown /metadata_parser/ )。

使用_index.md文件的分类配置

在最高分类法内,可以创建一个可以包含分类法配置的_index.md (通过元数据映射)以及分类学存档页面的可选PardallMarkdown.Content.Post内容,该文件的内容可保存到PardallMarkdown.Content.Link.index_post中。

_index数据映射可能包含:

  • :title :覆盖分类学标题/名称。
  • :sort_by :儿童发布分类规则(对于本分类法内的所有级别中的所有帖子)。接受的值:title | :date | :position
  • :sort_order :接受的值:desc | :asc
  • 任何其他额外的属性将保存到分类法的PardallMarkdown.Content.Link.index_post.metadata字段中。

请注意, _index文件不能通过slug调用,即"/taxonomy/-index" ,而必须获得分类法,并通过PardallMarkdown.Content.Link.index_post访问文件并发布数据。

帖子和页面

每个标记文件都是帖子(一块内容),但是pardallmarkDown考虑了根文件夹中的文件"/"为“页面”,并在任何层次结构级别,“ post”中任何文件夹中的文件。页面与根部层次结构并排添加到内容树。

从结构上讲,它们是相同的,唯一的区别是它们的属性设置为PardallMarkdown.Content.Post.type: :post | :page

示例:

  • 页面:可以参考固定数据的单个唯一帖子,例如联系人或有关页面( /contact, /over等)。
  • 帖子:其他所有内容,包括博客文章,文档页面,Wiki页面等等,等等,这些内容至少在一个级别的分类学内部,例如: "/docs/introduction""/wiki/languages/english/verbs/to-eat"

内容层次结构,分类学,类别和部分

类别,分类法和网站部分均指相同的内容:包含帖子的文件夹的层次结构,这些文件夹中包含在其中,这又定义了帖子集或帖子组。

  • 分类/类别/部分/组名称来自文件夹名称,每个单词都大写。
  • 层次结构由嵌套文件夹定义。
  • 顶级分类法是第一级文件夹,例如: "/blog" ,因此"/blog/news/art""/blog"作为其最高分类法/父母。
  • 帖子单独保存(用PardallMarkdown.Repository.get_by_slug("/a/post/slug") )及其分类法和分类法的层次结构。分类档案库(分类法的所有职位)及其层次结构包含在PardallMarkdown.Content.Link.children中,当时分类学通过:
    • PardallMarkdown.Repository.get_by_slug("/taxonomy/inner-taxonomy")
    • PardallMarkdown.Repository.get_content_tree("/taxonomy/inner-taxonomy")
    • PardallMarkdown.Repository.get_content_tree("/") - root,其中包含所有分类法,其帖子和层次结构。
  • 当通过PardallMarkdown.Repository.get_by_slug("/taxonomy/inner-taxonomy")检索Slug的分类学时,分类学:children包含其所有最内部分类法的所有职位:children
    • 例如,帖子:“/blog/news/news/city/foo”出现在:children"/blog""/blog/news""/blog/news/city"中。
  • 另一方面,用PardallMarkdown.Repository.get_content_tree/1检索的内容树中的分类学仅包含其直接子女帖子。
    • 例如,帖子:“/blog/news/news/city/foo”仅在其定义分类法的:children中出现: "/blog/news/city"

考虑示例内容目录结构:

 content/
|   about.md
|   contact.md
└───blog/
|   |   _index.md
|   |   post1.md
|   |   post2.md
|   └───art/
|   |   └───traditional/
|   |       └───oil-paintings/
|   |           └───impressionism/
|   |               └───claude-monet/
|   |               └───pierre-auguste-renoir/
|   └───news/
|       |   .. posts..
|       └───city/
|       |   |   ..posts..
|       └───worldwide/
|           |   ..posts..
└───docs/
|   |   _index.md
|   |   getting-started.md
|   └───setup/
| ...more and more...

另外,请考虑docs/_index.md定义A: docs文件夹的:title ,该文件将覆盖默认命名约定。

将创建以下类别:

 ["Blog"]
["Blog"]["Art]
["Blog"]["Art]["Traditional"]
["Blog"]["Art]["Traditional"]["Oil Paintings"]
["Blog"]["Art]["Traditional"]["Oil Paintings"]["Impressionism"]
["Blog"]["Art]["Traditional"]["Oil Paintings"]["Impressionism"]["Claude Monet"]
["Blog"]["Art]["Traditional"]["Oil Paintings"]["Impressionism"]["Pierre Auguste Renoir"]
["Blog"]["News"]
["Blog"]["News"]["City]
["Blog"]["News"]["Worldwide]
["Documentation"]
["Documentation"]["Setup]

树木

每次重新编译内容时,都会生成三种树木。

这些树可用于导航内容,可以将其打印为链接列表等。检查演示项目,以获取如何使用树和HTML帮助者从树中生成链接的多个示例。

树木是:

分类树

一棵拥有所有分类法的树,用标题排序,并相应地嵌套。

分类树可以通过PardallMarkdown.Repository.get_taxonomy_tree/0检索。

内容树

一棵包含所有分类法的树,但他们的孩子帖子嵌套了:

  • 帖子位于其最内部的分类法下方。
  • 帖子以其最高的分类分类规则进行排序。

创建了多个内容树。一个单一的“主”内容树,可通过root slug "/"获得每个分类学级别的内容树。例如,文档层次结构的内容树,其中包含指向所有子层次结构和帖子的链接。

内容树可以通过PardallMarkdown.Repository.get_content_tree/1检索。

导航后

在当前帖子之后,将所有帖子内部的链接插入了上一个和下一个帖子。这些链接在PardallMarkdown.Content.Post.link.previousPardallMarkdown.Content.Post.link.next中。

目录

每个帖子都包含自己的自动生成的目录树,可在邮政的PardallMarkdown.Content.Post.toc字段内使用。

背压

TODO:描述FileWatcher背压机制。

常问问题

如何将其与Phoenix和Phoenix Liveview集成?

单独的存储库中有一个演示项目:PardallmarkDown Phoenix Demo。

该演示项目还具有HTML帮助者,可以用链接打印出生成的内容,

下载源码

通过命令行克隆项目:

git clone https://github.com/alfredbaudisch/pardall_markdown.git