-
- {{ page.name }}
-
-
- {{ page.content }}
+ {{ page.summary }}
+
+
+
diff --git a/_plugins/reading_time_filter.rb b/_plugins/reading_time_filter.rb
new file mode 100644
index 0000000..a740a8e
--- /dev/null
+++ b/_plugins/reading_time_filter.rb
@@ -0,0 +1,55 @@
+module ReadingTimeFilter
+ include Liquid::StandardFilters
+
+ def reading_time(input)
+ # Get words count.
+ total_words = get_plain_text(input).split.size
+
+ # Load configuration.
+ config = @context.registers[:site].config["reading_time"]
+
+ # Setup default value.
+ if ! config
+ second_plural = "seconds"
+ minute_singular = "minute"
+ minute_plural = "minutes"
+ else
+ second_plural = config["second_plural"] ? config["second_plural"] : "seconds"
+ minute_singular = config["minute_singular"] ? config["minute_singular"] : "minute"
+ minute_plural = config["minute_plural"] ? config["minute_plural"] : "minutes"
+ end
+
+ # Average reading words per minute.
+ words_per_minute = 180
+
+ # Calculate reading time.
+ case total_words
+ when 0 .. 89
+ return "30 #{second_plural}"
+ when 90 .. 269
+ return "1 #{minute_singular}"
+ when 270 .. 449
+ return "2 #{minute_plural}"
+ when 450 .. 629
+ return "3 #{minute_plural}"
+ when 630 .. 809
+ return "4 #{minute_plural}"
+ when 810 .. 990
+ return "5 #{minute_plural}"
+ else
+ minutes = ( total_words / words_per_minute ).floor
+ return "#{minutes} #{minute_plural}";
+ end
+ end
+
+ def get_plain_text(input)
+ strip_html(strip_pre_tags(input))
+ end
+
+ def strip_pre_tags(input)
+ empty = ''.freeze
+ input.to_s.gsub(/
+ {{ page.content | inject_anchors }}
/mi, empty) + end + end + + Liquid::Template.register_filter(ReadingTimeFilter) \ No newline at end of file diff --git a/assets/css/_syntax.scss b/assets/css/_syntax.scss new file mode 100644 index 0000000..f3f22a8 --- /dev/null +++ b/assets/css/_syntax.scss @@ -0,0 +1,261 @@ +.highlight, html .gist .gist-file .gist-syntax .gist-highlight { + table td.code { width: 100%; } + border: 1px solid $pre-border !important; + } + .highlight .line-numbers, html .gist .gist-file .gist-syntax .highlight .line_numbers { + text-align: right; + font-size: 13px; + line-height: 1.45em; + @if $solarized == light { + background: lighten($base03, 1) $noise-bg !important; + border-right: 1px solid darken($base02, 2) !important; + @include box-shadow(lighten($base03, 2) -1px 0 inset); + text-shadow: lighten($base02, 2) 0 -1px; + } @else { + background: $base02 $noise-bg !important; + border-right: 1px solid darken($base03, 2) !important; + @include box-shadow(lighten($base02, 2) -1px 0 inset); + text-shadow: darken($base02, 10) 0 -1px; + } + span { color: $base01 !important; } + padding: .8em !important; + @include border-radius(0); + } + + figure.code, .gist-file, pre { + @include box-shadow(rgba(#000, .06) 0 0 10px); + .highlight pre { @include box-shadow(none); } + } + + .gist .highlight, figure.code .highlight { + @include selection(adjust-color($base03, $lightness: 23%, $saturation: -65%), $text-shadow: $base03 0 1px); + } + html .gist .gist-file { + margin-bottom: 1.8em; + position: relative; + border: none; + padding-top: image-height("code_bg.png") !important; + .highlight { + margin-bottom: 0; + } + .gist-syntax { + border-bottom: 0 !important; + background: none !important; + .gist-highlight { + background: $base03 !important; + } + .highlight pre { + @extend .pre-code; + padding: 0; + } + } + .gist-meta { + padding: .6em 0.8em; + border: 1px solid lighten($base02, 2) !important; + color: $base01; + font-size: .7em !important; + @if $solarized == light { + background: lighten($base03, 2) $noise-bg; + border: 1px solid $pre-border !important; + border-top: 1px solid lighten($base03, 2) !important; + } @else { + background: $base02 $noise-bg; + } + @extend .sans; + line-height: 1.5em; + a { + color: mix($base1, $base01) !important; + @include hover-link; + &:hover { color: $base1 !important; } + } + a[href*='#file'] { + position: absolute; top: 0; left:0; right:-10px; + color: #474747 !important; + @extend .code-title; + &:hover { color: $link-color !important; } + } + a[href*=raw]{ + @extend .download-source; + top: .4em; + } + } + } + pre { + background: $pre-bg $noise-bg; + @include border-radius(.4em); + @extend .mono; + border: 1px solid $pre-border; + line-height: 1.45em; + font-size: 13px; + margin-bottom: 2.1em; + padding: .8em 1em; + color: $pre-color; + overflow: auto; + } + h3.filename { + @extend .code-title; + + pre { @include border-top-radius(0px); } + } + + p, li { + code { + @extend .mono; + display: inline-block; + white-space: no-wrap; + background: #fff; + font-size: .8em; + line-height: 1.5em; + color: #555; + border: 1px solid #ddd; + @include border-radius(.4em); + padding: 0 .3em; + margin: -1px 0; + } + pre code { font-size: 1em !important; background: none; border: none; } + } + + .pre-code { + font-family: $mono !important; + overflow: scroll; + overflow-y: hidden; + display: block; + padding: .8em; + overflow-x: auto; + line-height: 1.45em; + background: $base03 $noise-bg !important; + color: $base1 !important; + span { color: $base1 !important; } + span { font-style: normal !important; font-weight: normal !important; } + + .c { color: $base01 !important; font-style: italic !important; } /* Comment */ + .cm { color: $base01 !important; font-style: italic !important; } /* Comment.Multiline */ + .cp { color: $base01 !important; font-style: italic !important; } /* Comment.Preproc */ + .c1 { color: $base01 !important; font-style: italic !important; } /* Comment.Single */ + .cs { color: $base01 !important; font-weight: bold !important; font-style: italic !important; } /* Comment.Special */ + .err { color: $solar-red !important; background: none !important; } /* Error */ + .k { color: $solar-orange !important; } /* Keyword */ + .o { color: $base1 !important; font-weight: bold !important; } /* Operator */ + .p { color: $base1 !important; } /* Operator */ + .ow { color: $solar-cyan !important; font-weight: bold !important; } /* Operator.Word */ + .gd { color: $base1 !important; background-color: mix($solar-red, $base03, 25%) !important; display: inline-block; } /* Generic.Deleted */ + .gd .x { color: $base1 !important; background-color: mix($solar-red, $base03, 35%) !important; display: inline-block; } /* Generic.Deleted.Specific */ + .ge { color: $base1 !important; font-style: italic !important; } /* Generic.Emph */ + //.gr { color: #aa0000 } /* Generic.Error */ + .gh { color: $base01 !important; } /* Generic.Heading */ + .gi { color: $base1 !important; background-color: mix($solar-green, $base03, 20%) !important; display: inline-block; } /* Generic.Inserted */ + .gi .x { color: $base1 !important; background-color: mix($solar-green, $base03, 40%) !important; display: inline-block; } /* Generic.Inserted.Specific */ + //.go { color: #888888 } /* Generic.Output */ + //.gp { color: #555555 } /* Generic.Prompt */ + .gs { color: $base1 !important; font-weight: bold !important; } /* Generic.Strong */ + .gu { color: $solar-violet !important; } /* Generic.Subheading */ + //.gt { color: #aa0000 } /* Generic.Traceback */ + .kc { color: $solar-green !important; font-weight: bold !important; } /* Keyword.Constant */ + .kd { color: $solar-blue !important; } /* Keyword.Declaration */ + .kp { color: $solar-orange !important; font-weight: bold !important; } /* Keyword.Pseudo */ + .kr { color: $solar-magenta !important; font-weight: bold !important; } /* Keyword.Reserved */ + .kt { color: $solar-cyan !important; } /* Keyword.Type */ + .n { color: $solar-blue !important; } + .na { color: $solar-blue !important; } /* Name.Attribute */ + .nb { color: $solar-green !important; } /* Name.Builtin */ + .nc { color: $solar-magenta !important;} /* Name.Class */ + .no { color: $solar-yellow !important; } /* Name.Constant */ + //.ni { color: #800080 } /* Name.Entity */ + .nl { color: $solar-green !important; } + .ne { color: $solar-blue !important; font-weight: bold !important; } /* Name.Exception */ + .nf { color: $solar-blue !important; font-weight: bold !important; } /* Name.Function */ + .nn { color: $solar-yellow !important; } /* Name.Namespace */ + .nt { color: $solar-blue !important; font-weight: bold !important; } /* Name.Tag */ + .nx { color: $solar-yellow !Important; } + //.bp { color: #999999 } /* Name.Builtin.Pseudo */ + //.vc { color: #008080 } /* Name.Variable.Class */ + .vg { color: $solar-blue !important; } /* Name.Variable.Global */ + .vi { color: $solar-blue !important; } /* Name.Variable.Instance */ + .nv { color: $solar-blue !important; } /* Name.Variable */ + //.w { color: #bbbbbb } /* Text.Whitespace */ + .mf { color: $solar-cyan !important; } /* Literal.Number.Float */ + .m { color: $solar-cyan !important; } /* Literal.Number */ + .mh { color: $solar-cyan !important; } /* Literal.Number.Hex */ + .mi { color: $solar-cyan !important; } /* Literal.Number.Integer */ + //.mo { color: #009999 } /* Literal.Number.Oct */ + .s { color: $solar-cyan !important; } /* Literal.String */ + //.sb { color: #d14 } /* Literal.String.Backtick */ + //.sc { color: #d14 } /* Literal.String.Char */ + .sd { color: $solar-cyan !important; } /* Literal.String.Doc */ + .s2 { color: $solar-cyan !important; } /* Literal.String.Double */ + .se { color: $solar-red !important; } /* Literal.String.Escape */ + //.sh { color: #d14 } /* Literal.String.Heredoc */ + .si { color: $solar-blue !important; } /* Literal.String.Interpol */ + //.sx { color: #d14 } /* Literal.String.Other */ + .sr { color: $solar-cyan !important; } /* Literal.String.Regex */ + .s1 { color: $solar-cyan !important; } /* Literal.String.Single */ + //.ss { color: #990073 } /* Literal.String.Symbol */ + //.il { color: #009999 } /* Literal.Number.Integer.Long */ + div { .gd, .gd .x, .gi, .gi .x { display: inline-block; width: 100%; }} + } + + .highlight, .gist-highlight { + pre { background: none; @include border-radius(0px); border: none; padding: 0; margin-bottom: 0; } + margin-bottom: 1.8em; + background: $base03; + overflow-y: hidden; + overflow-x: auto; + } + + $solar-scroll-bg: rgba(#fff, .15); + $solar-scroll-thumb: rgba(#fff, .2); + @if $solarized == light { + $solar-scroll-bg: rgba(#000, .15); + $solar-scroll-thumb: rgba(#000, .15); + } + + pre, .highlight, .gist-highlight { + &::-webkit-scrollbar { height: .5em; background: $solar-scroll-bg; } + &::-webkit-scrollbar-thumb:horizontal { background: $solar-scroll-thumb; -webkit-border-radius: 4px; border-radius: 4px } + } + + .highlight code { + @extend .pre-code; background: #000; + } + figure.code { + background: none; + padding: 0; + border: 0; + margin-bottom: 1.5em; + pre { margin-bottom: 0; } + figcaption { + position: relative; + @extend .code-title; + a { @extend .download-source; } + } + .highlight { + margin-bottom: 0; + } + } + + .code-title { + text-align: center; + font-size: 13px; + line-height: 2em; + text-shadow: #cbcccc 0 1px 0; + color: #474747; + font-weight: normal; + margin-bottom: 0; + @include border-top-radius(5px); + font-family: "Helvetica Neue", Arial, "Lucida Grande", "Lucida Sans Unicode", Lucida, sans-serif; + background: #aaaaaa image-url("code_bg.png") top repeat-x; + border: 1px solid #565656; + border-top-color: #cbcbcb; + border-left-color: #a5a5a5; + border-right-color: #a5a5a5; + border-bottom: 0; + } + + .download-source { + position: absolute; right: .8em; + @include hover-link; + color: #666 !important; + z-index: 1; + font-size: 13px; + text-shadow: #cbcccc 0 1px 0; + padding-left: 3em; + } \ No newline at end of file diff --git a/assets/css/components.css b/assets/css/components.css index ef80581..67cf95e 100644 --- a/assets/css/components.css +++ b/assets/css/components.css @@ -1,3 +1,5 @@ @import 'components/header.css'; @import 'components/hero.css'; -@import 'components/button.css'; \ No newline at end of file +@import 'components/button.css'; +@import 'components/card.css'; +@import 'components/toc.css'; \ No newline at end of file diff --git a/assets/css/components/card.css b/assets/css/components/card.css new file mode 100644 index 0000000..38c161c --- /dev/null +++ b/assets/css/components/card.css @@ -0,0 +1,46 @@ +.card { + border-radius: 5px; + border: 1px solid #DADADA; + padding: 16px; + margin-bottom: 32px; + background: #FFFFFF; + display: block; + color: #000000; + text-decoration: none; +} + +.card--selectable { + transition: all 230ms ease-in-out; +} + +.card--selectable:hover { + box-shadow: 0 1px 3px rgba(0, 0, 0, .07), + 0 2px 5px rgba(0, 0, 0, .09), + 0 4px 8px rgba(0, 0, 0, .11); + cursor: pointer; + transition: all 230ms ease-in-out; +} + +.card__header { + color: #000000; + font-weight: 600; + font-size: 1.4em; + margin-bottom: 8px; +} + +.card__header > a { + color: inherit; + text-decoration: none; +} + +.card__sub-header { + display: flex; + align-items: center; + gap: 16px; + margin-bottom: 8px; +} + +.card__sub-header > span { + display: flex; + gap: 4px; +} \ No newline at end of file diff --git a/assets/css/components/toc.css b/assets/css/components/toc.css new file mode 100644 index 0000000..0d8cf7f --- /dev/null +++ b/assets/css/components/toc.css @@ -0,0 +1,34 @@ +.nav--toc { + list-style-type: none; + margin: 0; + padding: 0; +} + +.nav--toc a { + color: var(--primary-color); +} + +.toc-entry { + font-weight: 500; + margin-bottom: 4px; + position: relative; +} + +.toc-entry::before { + content: ''; + position: absolute; + left: 0; + bottom: 0; + width: 0; + border-bottom: 2px solid var(--primary-color); + transition: all 230ms ease-in-out; +} + +.toc-entry a { + text-decoration: none; +} + +.toc-entry:hover::before { + width: 100%; + transition: all 230ms ease-in-out; +} diff --git a/assets/css/github-markdown.css b/assets/css/github-markdown.css new file mode 100644 index 0000000..e3895f1 --- /dev/null +++ b/assets/css/github-markdown.css @@ -0,0 +1,1195 @@ +@media (prefers-color-scheme: dark) { + .markdown-body, + [data-theme="dark"] { + /*dark*/ + color-scheme: dark; + --color-prettylights-syntax-comment: #8b949e; + --color-prettylights-syntax-constant: #79c0ff; + --color-prettylights-syntax-entity: #d2a8ff; + --color-prettylights-syntax-storage-modifier-import: #c9d1d9; + --color-prettylights-syntax-entity-tag: #7ee787; + --color-prettylights-syntax-keyword: #ff7b72; + --color-prettylights-syntax-string: #a5d6ff; + --color-prettylights-syntax-variable: #ffa657; + --color-prettylights-syntax-brackethighlighter-unmatched: #f85149; + --color-prettylights-syntax-invalid-illegal-text: #f0f6fc; + --color-prettylights-syntax-invalid-illegal-bg: #8e1519; + --color-prettylights-syntax-carriage-return-text: #f0f6fc; + --color-prettylights-syntax-carriage-return-bg: #b62324; + --color-prettylights-syntax-string-regexp: #7ee787; + --color-prettylights-syntax-markup-list: #f2cc60; + --color-prettylights-syntax-markup-heading: #1f6feb; + --color-prettylights-syntax-markup-italic: #c9d1d9; + --color-prettylights-syntax-markup-bold: #c9d1d9; + --color-prettylights-syntax-markup-deleted-text: #ffdcd7; + --color-prettylights-syntax-markup-deleted-bg: #67060c; + --color-prettylights-syntax-markup-inserted-text: #aff5b4; + --color-prettylights-syntax-markup-inserted-bg: #033a16; + --color-prettylights-syntax-markup-changed-text: #ffdfb6; + --color-prettylights-syntax-markup-changed-bg: #5a1e02; + --color-prettylights-syntax-markup-ignored-text: #c9d1d9; + --color-prettylights-syntax-markup-ignored-bg: #1158c7; + --color-prettylights-syntax-meta-diff-range: #d2a8ff; + --color-prettylights-syntax-brackethighlighter-angle: #8b949e; + --color-prettylights-syntax-sublimelinter-gutter-mark: #484f58; + --color-prettylights-syntax-constant-other-reference-link: #a5d6ff; + --color-fg-default: #e6edf3; + --color-fg-muted: #848d97; + --color-fg-subtle: #6e7681; + --color-canvas-default: #0d1117; + --color-canvas-subtle: #161b22; + --color-border-default: #30363d; + --color-border-muted: #21262d; + --color-neutral-muted: rgba(110,118,129,0.4); + --color-accent-fg: #2f81f7; + --color-accent-emphasis: #1f6feb; + --color-success-fg: #3fb950; + --color-success-emphasis: #238636; + --color-attention-fg: #d29922; + --color-attention-emphasis: #9e6a03; + --color-attention-subtle: rgba(187,128,9,0.15); + --color-danger-fg: #f85149; + --color-danger-emphasis: #da3633; + --color-done-fg: #a371f7; + --color-done-emphasis: #8957e5; + } +} + +@media (prefers-color-scheme: light) { + .markdown-body, + [data-theme="light"] { + /*light*/ + color-scheme: light; + --color-prettylights-syntax-comment: #57606a; + --color-prettylights-syntax-constant: #0550ae; + --color-prettylights-syntax-entity: #6639ba; + --color-prettylights-syntax-storage-modifier-import: #24292f; + --color-prettylights-syntax-entity-tag: #116329; + --color-prettylights-syntax-keyword: #cf222e; + --color-prettylights-syntax-string: #0a3069; + --color-prettylights-syntax-variable: #953800; + --color-prettylights-syntax-brackethighlighter-unmatched: #82071e; + --color-prettylights-syntax-invalid-illegal-text: #f6f8fa; + --color-prettylights-syntax-invalid-illegal-bg: #82071e; + --color-prettylights-syntax-carriage-return-text: #f6f8fa; + --color-prettylights-syntax-carriage-return-bg: #cf222e; + --color-prettylights-syntax-string-regexp: #116329; + --color-prettylights-syntax-markup-list: #3b2300; + --color-prettylights-syntax-markup-heading: #0550ae; + --color-prettylights-syntax-markup-italic: #24292f; + --color-prettylights-syntax-markup-bold: #24292f; + --color-prettylights-syntax-markup-deleted-text: #82071e; + --color-prettylights-syntax-markup-deleted-bg: #ffebe9; + --color-prettylights-syntax-markup-inserted-text: #116329; + --color-prettylights-syntax-markup-inserted-bg: #dafbe1; + --color-prettylights-syntax-markup-changed-text: #953800; + --color-prettylights-syntax-markup-changed-bg: #ffd8b5; + --color-prettylights-syntax-markup-ignored-text: #eaeef2; + --color-prettylights-syntax-markup-ignored-bg: #0550ae; + --color-prettylights-syntax-meta-diff-range: #8250df; + --color-prettylights-syntax-brackethighlighter-angle: #57606a; + --color-prettylights-syntax-sublimelinter-gutter-mark: #8c959f; + --color-prettylights-syntax-constant-other-reference-link: #0a3069; + --color-fg-default: #1F2328; + --color-fg-muted: #656d76; + --color-fg-subtle: #6e7781; + --color-canvas-default: #ffffff; + --color-canvas-subtle: rgb(51, 51, 51); + --color-border-default: #d0d7de; + --color-border-muted: hsla(210,18%,87%,1); + --color-neutral-muted: rgba(175,184,193,0.2); + --color-accent-fg: #0969da; + --color-accent-emphasis: #0969da; + --color-success-fg: #1a7f37; + --color-success-emphasis: #1f883d; + --color-attention-fg: #9a6700; + --color-attention-emphasis: #9a6700; + --color-attention-subtle: #fff8c5; + --color-danger-fg: #d1242f; + --color-danger-emphasis: #cf222e; + --color-done-fg: #8250df; + --color-done-emphasis: #8250df; + } +} + +.markdown-body { + -ms-text-size-adjust: 100%; + -webkit-text-size-adjust: 100%; + margin: 0; + color: var(--color-fg-default); + background-color: var(--color-canvas-default); + font-family: -apple-system,BlinkMacSystemFont,"Segoe UI","Noto Sans",Helvetica,Arial,sans-serif,"Apple Color Emoji","Segoe UI Emoji"; + font-size: 16px; + line-height: 1.5; + word-wrap: break-word; +} + +.markdown-body .octicon { + display: inline-block; + fill: currentColor; + vertical-align: text-bottom; +} + +.markdown-body h1:hover .anchor .octicon-link:before, +.markdown-body h2:hover .anchor .octicon-link:before, +.markdown-body h3:hover .anchor .octicon-link:before, +.markdown-body h4:hover .anchor .octicon-link:before, +.markdown-body h5:hover .anchor .octicon-link:before, +.markdown-body h6:hover .anchor .octicon-link:before { + width: 16px; + height: 16px; + content: ' '; + display: inline-block; + background-color: currentColor; + -webkit-mask-image: url("data:image/svg+xml,"); + mask-image: url("data:image/svg+xml,"); +} + +.markdown-body details, +.markdown-body figcaption, +.markdown-body figure { + display: block; +} + +.markdown-body summary { + display: list-item; +} + +.markdown-body [hidden] { + display: none !important; +} + +.markdown-body a { + background-color: transparent; + color: var(--color-accent-fg); + text-decoration: none; +} + +.markdown-body abbr[title] { + border-bottom: none; + -webkit-text-decoration: underline dotted; + text-decoration: underline dotted; +} + +.markdown-body b, +.markdown-body strong { + font-weight: var(--base-text-weight-semibold, 600); +} + +.markdown-body dfn { + font-style: italic; +} + +.markdown-body h1 { + margin: .67em 0; + font-weight: var(--base-text-weight-semibold, 600); + padding-bottom: .3em; + font-size: 2em; + border-bottom: 1px solid var(--color-border-muted); +} + +.markdown-body mark { + background-color: var(--color-attention-subtle); + color: var(--color-fg-default); +} + +.markdown-body small { + font-size: 90%; +} + +.markdown-body sub, +.markdown-body sup { + font-size: 75%; + line-height: 0; + position: relative; + vertical-align: baseline; +} + +.markdown-body sub { + bottom: -0.25em; +} + +.markdown-body sup { + top: -0.5em; +} + +.markdown-body img { + border-style: none; + max-width: 100%; + box-sizing: content-box; + background-color: var(--color-canvas-default); +} + +.markdown-body code, +.markdown-body kbd, +.markdown-body pre, +.markdown-body samp { + font-family: monospace; + font-size: 1em; +} + +.markdown-body figure { + margin: 1em 40px; +} + +.markdown-body hr { + box-sizing: content-box; + overflow: hidden; + background: transparent; + border-bottom: 1px solid var(--color-border-muted); + height: .25em; + padding: 0; + margin: 24px 0; + background-color: var(--color-border-default); + border: 0; +} + +.markdown-body input { + font: inherit; + margin: 0; + overflow: visible; + font-family: inherit; + font-size: inherit; + line-height: inherit; +} + +.markdown-body [type=button], +.markdown-body [type=reset], +.markdown-body [type=submit] { + -webkit-appearance: button; + appearance: button; +} + +.markdown-body [type=checkbox], +.markdown-body [type=radio] { + box-sizing: border-box; + padding: 0; +} + +.markdown-body [type=number]::-webkit-inner-spin-button, +.markdown-body [type=number]::-webkit-outer-spin-button { + height: auto; +} + +.markdown-body [type=search]::-webkit-search-cancel-button, +.markdown-body [type=search]::-webkit-search-decoration { + -webkit-appearance: none; + appearance: none; +} + +.markdown-body ::-webkit-input-placeholder { + color: inherit; + opacity: .54; +} + +.markdown-body ::-webkit-file-upload-button { + -webkit-appearance: button; + appearance: button; + font: inherit; +} + +.markdown-body a:hover { + text-decoration: underline; +} + +.markdown-body ::placeholder { + color: var(--color-fg-subtle); + opacity: 1; +} + +.markdown-body hr::before { + display: table; + content: ""; +} + +.markdown-body hr::after { + display: table; + clear: both; + content: ""; +} + +.markdown-body table { + border-spacing: 0; + border-collapse: collapse; + display: block; + width: max-content; + max-width: 100%; + overflow: auto; +} + +.markdown-body td, +.markdown-body th { + padding: 0; +} + +.markdown-body details summary { + cursor: pointer; +} + +.markdown-body details:not([open])>*:not(summary) { + display: none !important; +} + +.markdown-body a:focus, +.markdown-body [role=button]:focus, +.markdown-body input[type=radio]:focus, +.markdown-body input[type=checkbox]:focus { + outline: 2px solid var(--color-accent-fg); + outline-offset: -2px; + box-shadow: none; +} + +.markdown-body a:focus:not(:focus-visible), +.markdown-body [role=button]:focus:not(:focus-visible), +.markdown-body input[type=radio]:focus:not(:focus-visible), +.markdown-body input[type=checkbox]:focus:not(:focus-visible) { + outline: solid 1px transparent; +} + +.markdown-body a:focus-visible, +.markdown-body [role=button]:focus-visible, +.markdown-body input[type=radio]:focus-visible, +.markdown-body input[type=checkbox]:focus-visible { + outline: 2px solid var(--color-accent-fg); + outline-offset: -2px; + box-shadow: none; +} + +.markdown-body a:not([class]):focus, +.markdown-body a:not([class]):focus-visible, +.markdown-body input[type=radio]:focus, +.markdown-body input[type=radio]:focus-visible, +.markdown-body input[type=checkbox]:focus, +.markdown-body input[type=checkbox]:focus-visible { + outline-offset: 0; +} + +.markdown-body kbd { + display: inline-block; + padding: 3px 5px; + font: 11px ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace; + line-height: 10px; + color: var(--color-fg-default); + vertical-align: middle; + background-color: var(--color-canvas-subtle); + border: solid 1px var(--color-neutral-muted); + border-bottom-color: var(--color-neutral-muted); + border-radius: 6px; + box-shadow: inset 0 -1px 0 var(--color-neutral-muted); +} + +.markdown-body h1, +.markdown-body h2, +.markdown-body h3, +.markdown-body h4, +.markdown-body h5, +.markdown-body h6 { + margin-top: 24px; + margin-bottom: 16px; + font-weight: var(--base-text-weight-semibold, 600); + line-height: 1.25; +} + +.markdown-body h2 { + font-weight: var(--base-text-weight-semibold, 600); + padding-bottom: .3em; + font-size: 1.5em; + border-bottom: 1px solid var(--color-border-muted); +} + +.markdown-body h3 { + font-weight: var(--base-text-weight-semibold, 600); + font-size: 1.25em; +} + +.markdown-body h4 { + font-weight: var(--base-text-weight-semibold, 600); + font-size: 1em; +} + +.markdown-body h5 { + font-weight: var(--base-text-weight-semibold, 600); + font-size: .875em; +} + +.markdown-body h6 { + font-weight: var(--base-text-weight-semibold, 600); + font-size: .85em; + color: var(--color-fg-muted); +} + +.markdown-body p { + margin-top: 0; + margin-bottom: 10px; +} + +.markdown-body blockquote { + margin: 0; + padding: 0 1em; + color: var(--color-fg-muted); + border-left: .25em solid var(--color-border-default); +} + +.markdown-body ul, +.markdown-body ol { + margin-top: 0; + margin-bottom: 0; + padding-left: 2em; +} + +.markdown-body ol ol, +.markdown-body ul ol { + list-style-type: lower-roman; +} + +.markdown-body ul ul ol, +.markdown-body ul ol ol, +.markdown-body ol ul ol, +.markdown-body ol ol ol { + list-style-type: lower-alpha; +} + +.markdown-body dd { + margin-left: 0; +} + +.markdown-body tt, +.markdown-body code, +.markdown-body samp { + font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace; + font-size: 12px; +} + +.markdown-body pre { + margin-top: 0; + margin-bottom: 0; + font-family: ui-monospace,SFMono-Regular,SF Mono,Menlo,Consolas,Liberation Mono,monospace; + font-size: 12px; + word-wrap: normal; +} + +.markdown-body .octicon { + display: inline-block; + overflow: visible !important; + vertical-align: text-bottom; + fill: currentColor; +} + +.markdown-body input::-webkit-outer-spin-button, +.markdown-body input::-webkit-inner-spin-button { + margin: 0; + -webkit-appearance: none; + appearance: none; +} + +.markdown-body .mr-2 { + margin-right: var(--base-size-8, 8px) !important; +} + +.markdown-body::before { + display: table; + content: ""; +} + +.markdown-body::after { + display: table; + clear: both; + content: ""; +} + +.markdown-body>*:first-child { + margin-top: 0 !important; +} + +.markdown-body>*:last-child { + margin-bottom: 0 !important; +} + +.markdown-body a:not([href]) { + color: inherit; + text-decoration: none; +} + +.markdown-body .absent { + color: var(--color-danger-fg); +} + +.markdown-body .anchor { + float: left; + padding-right: 4px; + margin-left: -20px; + line-height: 1; +} + +.markdown-body .anchor:focus { + outline: none; +} + +.markdown-body p, +.markdown-body blockquote, +.markdown-body ul, +.markdown-body ol, +.markdown-body dl, +.markdown-body table, +.markdown-body pre, +.markdown-body details { + margin-top: 0; + margin-bottom: 16px; +} + +.markdown-body blockquote>:first-child { + margin-top: 0; +} + +.markdown-body blockquote>:last-child { + margin-bottom: 0; +} + +.markdown-body h1 .octicon-link, +.markdown-body h2 .octicon-link, +.markdown-body h3 .octicon-link, +.markdown-body h4 .octicon-link, +.markdown-body h5 .octicon-link, +.markdown-body h6 .octicon-link { + color: var(--color-fg-default); + vertical-align: middle; + visibility: hidden; +} + +.markdown-body h1:hover .anchor, +.markdown-body h2:hover .anchor, +.markdown-body h3:hover .anchor, +.markdown-body h4:hover .anchor, +.markdown-body h5:hover .anchor, +.markdown-body h6:hover .anchor { + text-decoration: none; +} + +.markdown-body h1:hover .anchor .octicon-link, +.markdown-body h2:hover .anchor .octicon-link, +.markdown-body h3:hover .anchor .octicon-link, +.markdown-body h4:hover .anchor .octicon-link, +.markdown-body h5:hover .anchor .octicon-link, +.markdown-body h6:hover .anchor .octicon-link { + visibility: visible; +} + +.markdown-body h1 tt, +.markdown-body h1 code, +.markdown-body h2 tt, +.markdown-body h2 code, +.markdown-body h3 tt, +.markdown-body h3 code, +.markdown-body h4 tt, +.markdown-body h4 code, +.markdown-body h5 tt, +.markdown-body h5 code, +.markdown-body h6 tt, +.markdown-body h6 code { + padding: 0 .2em; + font-size: inherit; +} + +.markdown-body summary h1, +.markdown-body summary h2, +.markdown-body summary h3, +.markdown-body summary h4, +.markdown-body summary h5, +.markdown-body summary h6 { + display: inline-block; +} + +.markdown-body summary h1 .anchor, +.markdown-body summary h2 .anchor, +.markdown-body summary h3 .anchor, +.markdown-body summary h4 .anchor, +.markdown-body summary h5 .anchor, +.markdown-body summary h6 .anchor { + margin-left: -40px; +} + +.markdown-body summary h1, +.markdown-body summary h2 { + padding-bottom: 0; + border-bottom: 0; +} + +.markdown-body ul.no-list, +.markdown-body ol.no-list { + padding: 0; + list-style-type: none; +} + +.markdown-body ol[type="a s"] { + list-style-type: lower-alpha; +} + +.markdown-body ol[type="A s"] { + list-style-type: upper-alpha; +} + +.markdown-body ol[type="i s"] { + list-style-type: lower-roman; +} + +.markdown-body ol[type="I s"] { + list-style-type: upper-roman; +} + +.markdown-body ol[type="1"] { + list-style-type: decimal; +} + +.markdown-body div>ol:not([type]) { + list-style-type: decimal; +} + +.markdown-body ul ul, +.markdown-body ul ol, +.markdown-body ol ol, +.markdown-body ol ul { + margin-top: 0; + margin-bottom: 0; +} + +.markdown-body li>p { + margin-top: 16px; +} + +.markdown-body li+li { + margin-top: .25em; +} + +.markdown-body dl { + padding: 0; +} + +.markdown-body dl dt { + padding: 0; + margin-top: 16px; + font-size: 1em; + font-style: italic; + font-weight: var(--base-text-weight-semibold, 600); +} + +.markdown-body dl dd { + padding: 0 16px; + margin-bottom: 16px; +} + +.markdown-body table th { + font-weight: var(--base-text-weight-semibold, 600); +} + +.markdown-body table th, +.markdown-body table td { + padding: 6px 13px; + border: 1px solid var(--color-border-default); +} + +.markdown-body table td>:last-child { + margin-bottom: 0; +} + +.markdown-body table tr { + background-color: var(--color-canvas-default); + border-top: 1px solid var(--color-border-muted); +} + +.markdown-body table tr:nth-child(2n) { + background-color: var(--color-canvas-subtle); +} + +.markdown-body table img { + background-color: transparent; +} + +.markdown-body img[align=right] { + padding-left: 20px; +} + +.markdown-body img[align=left] { + padding-right: 20px; +} + +.markdown-body .emoji { + max-width: none; + vertical-align: text-top; + background-color: transparent; +} + +.markdown-body span.frame { + display: block; + overflow: hidden; +} + +.markdown-body span.frame>span { + display: block; + float: left; + width: auto; + padding: 7px; + margin: 13px 0 0; + overflow: hidden; + border: 1px solid var(--color-border-default); +} + +.markdown-body span.frame span img { + display: block; + float: left; +} + +.markdown-body span.frame span span { + display: block; + padding: 5px 0 0; + clear: both; + color: var(--color-fg-default); +} + +.markdown-body span.align-center { + display: block; + overflow: hidden; + clear: both; +} + +.markdown-body span.align-center>span { + display: block; + margin: 13px auto 0; + overflow: hidden; + text-align: center; +} + +.markdown-body span.align-center span img { + margin: 0 auto; + text-align: center; +} + +.markdown-body span.align-right { + display: block; + overflow: hidden; + clear: both; +} + +.markdown-body span.align-right>span { + display: block; + margin: 13px 0 0; + overflow: hidden; + text-align: right; +} + +.markdown-body span.align-right span img { + margin: 0; + text-align: right; +} + +.markdown-body span.float-left { + display: block; + float: left; + margin-right: 13px; + overflow: hidden; +} + +.markdown-body span.float-left span { + margin: 13px 0 0; +} + +.markdown-body span.float-right { + display: block; + float: right; + margin-left: 13px; + overflow: hidden; +} + +.markdown-body span.float-right>span { + display: block; + margin: 13px auto 0; + overflow: hidden; + text-align: right; +} + +.markdown-body code, +.markdown-body tt { + padding: .2em .4em; + margin: 0; + font-size: 85%; + white-space: break-spaces; + background-color: var(--color-neutral-muted); + border-radius: 6px; +} + +.markdown-body code br, +.markdown-body tt br { + display: none; +} + +.markdown-body del code { + text-decoration: inherit; +} + +.markdown-body samp { + font-size: 85%; +} + +.markdown-body pre code { + font-size: 100%; +} + +.markdown-body pre>code { + padding: 0; + margin: 0; + word-break: normal; + white-space: pre; + background: transparent; + border: 0; +} + +.markdown-body .highlight { + margin-bottom: 16px; +} + +.markdown-body .highlight pre { + margin-bottom: 0; + word-break: normal; +} + +.markdown-body .highlight pre, +.markdown-body pre { + padding: 16px; + overflow: auto; + font-size: 85%; + line-height: 1.45; + color: var(--color-fg-default); + background-color: var(--color-canvas-subtle); + border-radius: 6px; +} + +.markdown-body pre code, +.markdown-body pre tt { + display: inline; + max-width: auto; + padding: 0; + margin: 0; + overflow: visible; + line-height: inherit; + word-wrap: normal; + background-color: transparent; + border: 0; +} + +.markdown-body .csv-data td, +.markdown-body .csv-data th { + padding: 5px; + overflow: hidden; + font-size: 12px; + line-height: 1; + text-align: left; + white-space: nowrap; +} + +.markdown-body .csv-data .blob-num { + padding: 10px 8px 9px; + text-align: right; + background: var(--color-canvas-default); + border: 0; +} + +.markdown-body .csv-data tr { + border-top: 0; +} + +.markdown-body .csv-data th { + font-weight: var(--base-text-weight-semibold, 600); + background: var(--color-canvas-subtle); + border-top: 0; +} + +.markdown-body [data-footnote-ref]::before { + content: "["; +} + +.markdown-body [data-footnote-ref]::after { + content: "]"; +} + +.markdown-body .footnotes { + font-size: 12px; + color: var(--color-fg-muted); + border-top: 1px solid var(--color-border-default); +} + +.markdown-body .footnotes ol { + padding-left: 16px; +} + +.markdown-body .footnotes ol ul { + display: inline-block; + padding-left: 16px; + margin-top: 16px; +} + +.markdown-body .footnotes li { + position: relative; +} + +.markdown-body .footnotes li:target::before { + position: absolute; + top: -8px; + right: -8px; + bottom: -8px; + left: -24px; + pointer-events: none; + content: ""; + border: 2px solid var(--color-accent-emphasis); + border-radius: 6px; +} + +.markdown-body .footnotes li:target { + color: var(--color-fg-default); +} + +.markdown-body .footnotes .data-footnote-backref g-emoji { + font-family: monospace; +} + +.markdown-body .pl-c { + color: var(--color-prettylights-syntax-comment); +} + +.markdown-body .pl-c1, +.markdown-body .pl-s .pl-v { + color: var(--color-prettylights-syntax-constant); +} + +.markdown-body .pl-e, +.markdown-body .pl-en { + color: var(--color-prettylights-syntax-entity); +} + +.markdown-body .pl-smi, +.markdown-body .pl-s .pl-s1 { + color: var(--color-prettylights-syntax-storage-modifier-import); +} + +.markdown-body .pl-ent { + color: var(--color-prettylights-syntax-entity-tag); +} + +.markdown-body .pl-k { + color: var(--color-prettylights-syntax-keyword); +} + +.markdown-body .pl-s, +.markdown-body .pl-pds, +.markdown-body .pl-s .pl-pse .pl-s1, +.markdown-body .pl-sr, +.markdown-body .pl-sr .pl-cce, +.markdown-body .pl-sr .pl-sre, +.markdown-body .pl-sr .pl-sra { + color: var(--color-prettylights-syntax-string); +} + +.markdown-body .pl-v, +.markdown-body .pl-smw { + color: var(--color-prettylights-syntax-variable); +} + +.markdown-body .pl-bu { + color: var(--color-prettylights-syntax-brackethighlighter-unmatched); +} + +.markdown-body .pl-ii { + color: var(--color-prettylights-syntax-invalid-illegal-text); + background-color: var(--color-prettylights-syntax-invalid-illegal-bg); +} + +.markdown-body .pl-c2 { + color: var(--color-prettylights-syntax-carriage-return-text); + background-color: var(--color-prettylights-syntax-carriage-return-bg); +} + +.markdown-body .pl-sr .pl-cce { + font-weight: bold; + color: var(--color-prettylights-syntax-string-regexp); +} + +.markdown-body .pl-ml { + color: var(--color-prettylights-syntax-markup-list); +} + +.markdown-body .pl-mh, +.markdown-body .pl-mh .pl-en, +.markdown-body .pl-ms { + font-weight: bold; + color: var(--color-prettylights-syntax-markup-heading); +} + +.markdown-body .pl-mi { + font-style: italic; + color: var(--color-prettylights-syntax-markup-italic); +} + +.markdown-body .pl-mb { + font-weight: bold; + color: var(--color-prettylights-syntax-markup-bold); +} + +.markdown-body .pl-md { + color: var(--color-prettylights-syntax-markup-deleted-text); + background-color: var(--color-prettylights-syntax-markup-deleted-bg); +} + +.markdown-body .pl-mi1 { + color: var(--color-prettylights-syntax-markup-inserted-text); + background-color: var(--color-prettylights-syntax-markup-inserted-bg); +} + +.markdown-body .pl-mc { + color: var(--color-prettylights-syntax-markup-changed-text); + background-color: var(--color-prettylights-syntax-markup-changed-bg); +} + +.markdown-body .pl-mi2 { + color: var(--color-prettylights-syntax-markup-ignored-text); + background-color: var(--color-prettylights-syntax-markup-ignored-bg); +} + +.markdown-body .pl-mdr { + font-weight: bold; + color: var(--color-prettylights-syntax-meta-diff-range); +} + +.markdown-body .pl-ba { + color: var(--color-prettylights-syntax-brackethighlighter-angle); +} + +.markdown-body .pl-sg { + color: var(--color-prettylights-syntax-sublimelinter-gutter-mark); +} + +.markdown-body .pl-corl { + text-decoration: underline; + color: var(--color-prettylights-syntax-constant-other-reference-link); +} + +.markdown-body g-emoji { + display: inline-block; + min-width: 1ch; + font-family: "Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol"; + font-size: 1em; + font-style: normal !important; + font-weight: var(--base-text-weight-normal, 400); + line-height: 1; + vertical-align: -0.075em; +} + +.markdown-body g-emoji img { + width: 1em; + height: 1em; +} + +.markdown-body .task-list-item { + list-style-type: none; +} + +.markdown-body .task-list-item label { + font-weight: var(--base-text-weight-normal, 400); +} + +.markdown-body .task-list-item.enabled label { + cursor: pointer; +} + +.markdown-body .task-list-item+.task-list-item { + margin-top: 4px; +} + +.markdown-body .task-list-item .handle { + display: none; +} + +.markdown-body .task-list-item-checkbox { + margin: 0 .2em .25em -1.4em; + vertical-align: middle; +} + +.markdown-body .contains-task-list:dir(rtl) .task-list-item-checkbox { + margin: 0 -1.6em .25em .2em; +} + +.markdown-body .contains-task-list { + position: relative; +} + +.markdown-body .contains-task-list:hover .task-list-item-convert-container, +.markdown-body .contains-task-list:focus-within .task-list-item-convert-container { + display: block; + width: auto; + height: 24px; + overflow: visible; + clip: auto; +} + +.markdown-body ::-webkit-calendar-picker-indicator { + filter: invert(50%); +} + +.markdown-body .markdown-alert { + padding: var(--base-size-8) var(--base-size-16); + margin-bottom: 16px; + color: inherit; + border-left: .25em solid var(--color-border-default); +} + +.markdown-body .markdown-alert>:first-child { + margin-top: 0; +} + +.markdown-body .markdown-alert>:last-child { + margin-bottom: 0; +} + +.markdown-body .markdown-alert .markdown-alert-title { + display: flex; + font-weight: var(--base-text-weight-medium, 500); + align-items: center; + line-height: 1; +} + +.markdown-body .markdown-alert.markdown-alert-note { + border-left-color: var(--color-accent-emphasis); +} + +.markdown-body .markdown-alert.markdown-alert-note .markdown-alert-title { + color: var(--color-accent-fg); +} + +.markdown-body .markdown-alert.markdown-alert-important { + border-left-color: var(--color-done-emphasis); +} + +.markdown-body .markdown-alert.markdown-alert-important .markdown-alert-title { + color: var(--color-done-fg); +} + +.markdown-body .markdown-alert.markdown-alert-warning { + border-left-color: var(--color-attention-emphasis); +} + +.markdown-body .markdown-alert.markdown-alert-warning .markdown-alert-title { + color: var(--color-attention-fg); +} + +.markdown-body .markdown-alert.markdown-alert-tip { + border-left-color: var(--color-success-emphasis); +} + +.markdown-body .markdown-alert.markdown-alert-tip .markdown-alert-title { + color: var(--color-success-fg); +} + +.markdown-body .markdown-alert.markdown-alert-caution { + border-left-color: var(--color-danger-emphasis); +} + +.markdown-body .markdown-alert.markdown-alert-caution .markdown-alert-title { + color: var(--color-danger-fg); +} diff --git a/assets/css/layout.css b/assets/css/layout.css index a1f2830..e3a34f3 100644 --- a/assets/css/layout.css +++ b/assets/css/layout.css @@ -10,4 +10,21 @@ .flex { flex: 1; display: block; +} + +.content { + margin: 32px 64px; +} + +.content--sidebar { + display: flex; + gap: 32px; +} + +.content aside { + width: 320px; +} + +.main { + flex: 1; } \ No newline at end of file diff --git a/assets/css/main.css b/assets/css/main.css index 397f28b..19c091e 100644 --- a/assets/css/main.css +++ b/assets/css/main.css @@ -1,9 +1,11 @@ @import url('https://fonts.googleapis.com/css2?family=Open+Sans:wght@300;400;500;600;800&display=swap'); @import 'components.css'; @import 'layout.css'; +@import 'pages.css'; :root { --primary-color: #29639f; + --color-canvas-subtle: rgb(51, 51, 51); } html, @@ -11,7 +13,11 @@ body { margin: 0; padding: 0; width: 100%; - height: 100vh; + height: 100%; +} + +body { + background: #F8F6F4; } * { @@ -33,7 +39,6 @@ body { color: #FFFFFF; } - .contact__container { display: flex; align-items: center; diff --git a/assets/css/pages.css b/assets/css/pages.css index e69de29..815dad9 100644 --- a/assets/css/pages.css +++ b/assets/css/pages.css @@ -0,0 +1,3 @@ +.guides { + flex: 1; +} \ No newline at end of file diff --git a/assets/css/syntax.css b/assets/css/syntax.css new file mode 100644 index 0000000..a96be73 --- /dev/null +++ b/assets/css/syntax.css @@ -0,0 +1,70 @@ +.highlight pre { background-color: #333; } +.highlight .hll { background-color: #333333 } +.highlight .c { color: #008800; font-style: italic; background-color: #0f140f } /* Comment */ +.highlight .err { color: #ffffff } /* Error */ +.highlight .g { color: #ffffff } /* Generic */ +.highlight .k { color: #fb660a; font-weight: bold } /* Keyword */ +.highlight .l { color: #ffffff } /* Literal */ +.highlight .n { color: #ffffff } /* Name */ +.highlight .o { color: #ffffff } /* Operator */ +.highlight .x { color: #ffffff } /* Other */ +.highlight .p { color: #ffffff } /* Punctuation */ +.highlight .cm { color: #008800; font-style: italic; background-color: #0f140f } /* Comment.Multiline */ +.highlight .cp { color: #ff0007; font-weight: bold; font-style: italic; background-color: #0f140f } /* Comment.Preproc */ +.highlight .c1 { color: #008800; font-style: italic; background-color: #0f140f } /* Comment.Single */ +.highlight .cs { color: #008800; font-style: italic; background-color: #0f140f } /* Comment.Special */ +.highlight .gd { color: #ffffff } /* Generic.Deleted */ +.highlight .ge { color: #ffffff } /* Generic.Emph */ +.highlight .gr { color: #ffffff } /* Generic.Error */ +.highlight .gh { color: #ffffff; font-weight: bold } /* Generic.Heading */ +.highlight .gi { color: #ffffff } /* Generic.Inserted */ +.highlight .go { color: #444444; background-color: #222222 } /* Generic.Output */ +.highlight .gp { color: #ffffff } /* Generic.Prompt */ +.highlight .gs { color: #ffffff } /* Generic.Strong */ +.highlight .gu { color: #ffffff; font-weight: bold } /* Generic.Subheading */ +.highlight .gt { color: #ffffff } /* Generic.Traceback */ +.highlight .kc { color: #fb660a; font-weight: bold } /* Keyword.Constant */ +.highlight .kd { color: #fb660a; font-weight: bold } /* Keyword.Declaration */ +.highlight .kn { color: #fb660a; font-weight: bold } /* Keyword.Namespace */ +.highlight .kp { color: #fb660a } /* Keyword.Pseudo */ +.highlight .kr { color: #fb660a; font-weight: bold } /* Keyword.Reserved */ +.highlight .kt { color: #cdcaa9; font-weight: bold } /* Keyword.Type */ +.highlight .ld { color: #ffffff } /* Literal.Date */ +.highlight .m { color: #0086f7; font-weight: bold } /* Literal.Number */ +.highlight .s { color: #0086d2 } /* Literal.String */ +.highlight .na { color: #ff0086; font-weight: bold } /* Name.Attribute */ +.highlight .nb { color: #ffffff } /* Name.Builtin */ +.highlight .nc { color: #ffffff } /* Name.Class */ +.highlight .no { color: #0086d2 } /* Name.Constant */ +.highlight .nd { color: #ffffff } /* Name.Decorator */ +.highlight .ni { color: #ffffff } /* Name.Entity */ +.highlight .ne { color: #ffffff } /* Name.Exception */ +.highlight .nf { color: #ff0086; font-weight: bold } /* Name.Function */ +.highlight .nl { color: #ffffff } /* Name.Label */ +.highlight .nn { color: #ffffff } /* Name.Namespace */ +.highlight .nx { color: #ffffff } /* Name.Other */ +.highlight .py { color: #ffffff } /* Name.Property */ +.highlight .nt { color: #fb660a; font-weight: bold } /* Name.Tag */ +.highlight .nv { color: #fb660a } /* Name.Variable */ +.highlight .ow { color: #ffffff } /* Operator.Word */ +.highlight .w { color: #888888 } /* Text.Whitespace */ +.highlight .mf { color: #0086f7; font-weight: bold } /* Literal.Number.Float */ +.highlight .mh { color: #0086f7; font-weight: bold } /* Literal.Number.Hex */ +.highlight .mi { color: #0086f7; font-weight: bold } /* Literal.Number.Integer */ +.highlight .mo { color: #0086f7; font-weight: bold } /* Literal.Number.Oct */ +.highlight .sb { color: #0086d2 } /* Literal.String.Backtick */ +.highlight .sc { color: #0086d2 } /* Literal.String.Char */ +.highlight .sd { color: #0086d2 } /* Literal.String.Doc */ +.highlight .s2 { color: #0086d2 } /* Literal.String.Double */ +.highlight .se { color: #0086d2 } /* Literal.String.Escape */ +.highlight .sh { color: #0086d2 } /* Literal.String.Heredoc */ +.highlight .si { color: #0086d2 } /* Literal.String.Interpol */ +.highlight .sx { color: #0086d2 } /* Literal.String.Other */ +.highlight .sr { color: #0086d2 } /* Literal.String.Regex */ +.highlight .s1 { color: #0086d2 } /* Literal.String.Single */ +.highlight .ss { color: #0086d2 } /* Literal.String.Symbol */ +.highlight .bp { color: #ffffff } /* Name.Builtin.Pseudo */ +.highlight .vc { color: #fb660a } /* Name.Variable.Class */ +.highlight .vg { color: #fb660a } /* Name.Variable.Global */ +.highlight .vi { color: #fb660a } /* Name.Variable.Instance */ +.highlight .il { color: #0086f7; font-weight: bold } /* Literal.Number.Integer.Long */ \ No newline at end of file diff --git a/assets/sass/_syntax.scss b/assets/sass/_syntax.scss new file mode 100644 index 0000000..f3f22a8 --- /dev/null +++ b/assets/sass/_syntax.scss @@ -0,0 +1,261 @@ +.highlight, html .gist .gist-file .gist-syntax .gist-highlight { + table td.code { width: 100%; } + border: 1px solid $pre-border !important; + } + .highlight .line-numbers, html .gist .gist-file .gist-syntax .highlight .line_numbers { + text-align: right; + font-size: 13px; + line-height: 1.45em; + @if $solarized == light { + background: lighten($base03, 1) $noise-bg !important; + border-right: 1px solid darken($base02, 2) !important; + @include box-shadow(lighten($base03, 2) -1px 0 inset); + text-shadow: lighten($base02, 2) 0 -1px; + } @else { + background: $base02 $noise-bg !important; + border-right: 1px solid darken($base03, 2) !important; + @include box-shadow(lighten($base02, 2) -1px 0 inset); + text-shadow: darken($base02, 10) 0 -1px; + } + span { color: $base01 !important; } + padding: .8em !important; + @include border-radius(0); + } + + figure.code, .gist-file, pre { + @include box-shadow(rgba(#000, .06) 0 0 10px); + .highlight pre { @include box-shadow(none); } + } + + .gist .highlight, figure.code .highlight { + @include selection(adjust-color($base03, $lightness: 23%, $saturation: -65%), $text-shadow: $base03 0 1px); + } + html .gist .gist-file { + margin-bottom: 1.8em; + position: relative; + border: none; + padding-top: image-height("code_bg.png") !important; + .highlight { + margin-bottom: 0; + } + .gist-syntax { + border-bottom: 0 !important; + background: none !important; + .gist-highlight { + background: $base03 !important; + } + .highlight pre { + @extend .pre-code; + padding: 0; + } + } + .gist-meta { + padding: .6em 0.8em; + border: 1px solid lighten($base02, 2) !important; + color: $base01; + font-size: .7em !important; + @if $solarized == light { + background: lighten($base03, 2) $noise-bg; + border: 1px solid $pre-border !important; + border-top: 1px solid lighten($base03, 2) !important; + } @else { + background: $base02 $noise-bg; + } + @extend .sans; + line-height: 1.5em; + a { + color: mix($base1, $base01) !important; + @include hover-link; + &:hover { color: $base1 !important; } + } + a[href*='#file'] { + position: absolute; top: 0; left:0; right:-10px; + color: #474747 !important; + @extend .code-title; + &:hover { color: $link-color !important; } + } + a[href*=raw]{ + @extend .download-source; + top: .4em; + } + } + } + pre { + background: $pre-bg $noise-bg; + @include border-radius(.4em); + @extend .mono; + border: 1px solid $pre-border; + line-height: 1.45em; + font-size: 13px; + margin-bottom: 2.1em; + padding: .8em 1em; + color: $pre-color; + overflow: auto; + } + h3.filename { + @extend .code-title; + + pre { @include border-top-radius(0px); } + } + + p, li { + code { + @extend .mono; + display: inline-block; + white-space: no-wrap; + background: #fff; + font-size: .8em; + line-height: 1.5em; + color: #555; + border: 1px solid #ddd; + @include border-radius(.4em); + padding: 0 .3em; + margin: -1px 0; + } + pre code { font-size: 1em !important; background: none; border: none; } + } + + .pre-code { + font-family: $mono !important; + overflow: scroll; + overflow-y: hidden; + display: block; + padding: .8em; + overflow-x: auto; + line-height: 1.45em; + background: $base03 $noise-bg !important; + color: $base1 !important; + span { color: $base1 !important; } + span { font-style: normal !important; font-weight: normal !important; } + + .c { color: $base01 !important; font-style: italic !important; } /* Comment */ + .cm { color: $base01 !important; font-style: italic !important; } /* Comment.Multiline */ + .cp { color: $base01 !important; font-style: italic !important; } /* Comment.Preproc */ + .c1 { color: $base01 !important; font-style: italic !important; } /* Comment.Single */ + .cs { color: $base01 !important; font-weight: bold !important; font-style: italic !important; } /* Comment.Special */ + .err { color: $solar-red !important; background: none !important; } /* Error */ + .k { color: $solar-orange !important; } /* Keyword */ + .o { color: $base1 !important; font-weight: bold !important; } /* Operator */ + .p { color: $base1 !important; } /* Operator */ + .ow { color: $solar-cyan !important; font-weight: bold !important; } /* Operator.Word */ + .gd { color: $base1 !important; background-color: mix($solar-red, $base03, 25%) !important; display: inline-block; } /* Generic.Deleted */ + .gd .x { color: $base1 !important; background-color: mix($solar-red, $base03, 35%) !important; display: inline-block; } /* Generic.Deleted.Specific */ + .ge { color: $base1 !important; font-style: italic !important; } /* Generic.Emph */ + //.gr { color: #aa0000 } /* Generic.Error */ + .gh { color: $base01 !important; } /* Generic.Heading */ + .gi { color: $base1 !important; background-color: mix($solar-green, $base03, 20%) !important; display: inline-block; } /* Generic.Inserted */ + .gi .x { color: $base1 !important; background-color: mix($solar-green, $base03, 40%) !important; display: inline-block; } /* Generic.Inserted.Specific */ + //.go { color: #888888 } /* Generic.Output */ + //.gp { color: #555555 } /* Generic.Prompt */ + .gs { color: $base1 !important; font-weight: bold !important; } /* Generic.Strong */ + .gu { color: $solar-violet !important; } /* Generic.Subheading */ + //.gt { color: #aa0000 } /* Generic.Traceback */ + .kc { color: $solar-green !important; font-weight: bold !important; } /* Keyword.Constant */ + .kd { color: $solar-blue !important; } /* Keyword.Declaration */ + .kp { color: $solar-orange !important; font-weight: bold !important; } /* Keyword.Pseudo */ + .kr { color: $solar-magenta !important; font-weight: bold !important; } /* Keyword.Reserved */ + .kt { color: $solar-cyan !important; } /* Keyword.Type */ + .n { color: $solar-blue !important; } + .na { color: $solar-blue !important; } /* Name.Attribute */ + .nb { color: $solar-green !important; } /* Name.Builtin */ + .nc { color: $solar-magenta !important;} /* Name.Class */ + .no { color: $solar-yellow !important; } /* Name.Constant */ + //.ni { color: #800080 } /* Name.Entity */ + .nl { color: $solar-green !important; } + .ne { color: $solar-blue !important; font-weight: bold !important; } /* Name.Exception */ + .nf { color: $solar-blue !important; font-weight: bold !important; } /* Name.Function */ + .nn { color: $solar-yellow !important; } /* Name.Namespace */ + .nt { color: $solar-blue !important; font-weight: bold !important; } /* Name.Tag */ + .nx { color: $solar-yellow !Important; } + //.bp { color: #999999 } /* Name.Builtin.Pseudo */ + //.vc { color: #008080 } /* Name.Variable.Class */ + .vg { color: $solar-blue !important; } /* Name.Variable.Global */ + .vi { color: $solar-blue !important; } /* Name.Variable.Instance */ + .nv { color: $solar-blue !important; } /* Name.Variable */ + //.w { color: #bbbbbb } /* Text.Whitespace */ + .mf { color: $solar-cyan !important; } /* Literal.Number.Float */ + .m { color: $solar-cyan !important; } /* Literal.Number */ + .mh { color: $solar-cyan !important; } /* Literal.Number.Hex */ + .mi { color: $solar-cyan !important; } /* Literal.Number.Integer */ + //.mo { color: #009999 } /* Literal.Number.Oct */ + .s { color: $solar-cyan !important; } /* Literal.String */ + //.sb { color: #d14 } /* Literal.String.Backtick */ + //.sc { color: #d14 } /* Literal.String.Char */ + .sd { color: $solar-cyan !important; } /* Literal.String.Doc */ + .s2 { color: $solar-cyan !important; } /* Literal.String.Double */ + .se { color: $solar-red !important; } /* Literal.String.Escape */ + //.sh { color: #d14 } /* Literal.String.Heredoc */ + .si { color: $solar-blue !important; } /* Literal.String.Interpol */ + //.sx { color: #d14 } /* Literal.String.Other */ + .sr { color: $solar-cyan !important; } /* Literal.String.Regex */ + .s1 { color: $solar-cyan !important; } /* Literal.String.Single */ + //.ss { color: #990073 } /* Literal.String.Symbol */ + //.il { color: #009999 } /* Literal.Number.Integer.Long */ + div { .gd, .gd .x, .gi, .gi .x { display: inline-block; width: 100%; }} + } + + .highlight, .gist-highlight { + pre { background: none; @include border-radius(0px); border: none; padding: 0; margin-bottom: 0; } + margin-bottom: 1.8em; + background: $base03; + overflow-y: hidden; + overflow-x: auto; + } + + $solar-scroll-bg: rgba(#fff, .15); + $solar-scroll-thumb: rgba(#fff, .2); + @if $solarized == light { + $solar-scroll-bg: rgba(#000, .15); + $solar-scroll-thumb: rgba(#000, .15); + } + + pre, .highlight, .gist-highlight { + &::-webkit-scrollbar { height: .5em; background: $solar-scroll-bg; } + &::-webkit-scrollbar-thumb:horizontal { background: $solar-scroll-thumb; -webkit-border-radius: 4px; border-radius: 4px } + } + + .highlight code { + @extend .pre-code; background: #000; + } + figure.code { + background: none; + padding: 0; + border: 0; + margin-bottom: 1.5em; + pre { margin-bottom: 0; } + figcaption { + position: relative; + @extend .code-title; + a { @extend .download-source; } + } + .highlight { + margin-bottom: 0; + } + } + + .code-title { + text-align: center; + font-size: 13px; + line-height: 2em; + text-shadow: #cbcccc 0 1px 0; + color: #474747; + font-weight: normal; + margin-bottom: 0; + @include border-top-radius(5px); + font-family: "Helvetica Neue", Arial, "Lucida Grande", "Lucida Sans Unicode", Lucida, sans-serif; + background: #aaaaaa image-url("code_bg.png") top repeat-x; + border: 1px solid #565656; + border-top-color: #cbcbcb; + border-left-color: #a5a5a5; + border-right-color: #a5a5a5; + border-bottom: 0; + } + + .download-source { + position: absolute; right: .8em; + @include hover-link; + color: #666 !important; + z-index: 1; + font-size: 13px; + text-shadow: #cbcccc 0 1px 0; + padding-left: 3em; + } \ No newline at end of file diff --git a/collections/_guides/create_plugin.md b/collections/_guides/create_plugin.md new file mode 100644 index 0000000..b408f41 --- /dev/null +++ b/collections/_guides/create_plugin.md @@ -0,0 +1,232 @@ +--- +difficulty: 2 +summary: Create a plugin for OpenSCD in just a couple of steps +title: How to write a plugin for OpenSCD +toc: true +layout: guide +cover: solar +--- + + +This tutorial will explain on how to write a plugin for OpenSCD. + +## Prerequisites +* Node ( LTS) +* Git +* An IDE + +In this example, we will use the Lit framework ([https://lit.dev](https://lit.dev)) to create a plugin for OpenSCD. On top of the Lit framework, we make use of the standards provided by Open Web Components ([open-wc.org](https://open-wc.org/)). + + +# Scaffolding a new plugin +We can scaffold a new empty project by using the `@open-wc` init command. + +{% highlight ts %} +npm init @open-wc +{% endhighlight %} + +--- + +When prompted, choose the following: + +`What would you like to do today?` › Scaffold a new project + +`What would you like to scaffold?` › Web Component + +`What would you like to add?` › Linting (eslint & prettier), Testing (web-test-runner), Demoing (storybook) + +`Would you like to use typescript?` › Yes + +`What is the tag name of your web component?` › oscd-my-new-plugin + +--- + +After scaffolding is complete, the first thing that we need to do is to change the plugin class. + +Open the `OscdMyNewPlugin.ts` file in the `src` folder and change the following line: + +{% highlight ts %} +- export class OscdMyNewPlugin extends LitElement { ++ export default class OscdMyNewPlugin extends LitElement { +{% endhighlight %} + +> Note the `default` keyword. + +--- + +Now, the `src/oscd-my-new-plugin.ts` file needs to be changed: + +{% highlight ts %} +- import { OscdMyNewPlugin } from './OscdMyNewPlugin.js'; ++ import OscdMyNewPlugin from './OscdMyNewPlugin.js'; +{% endhighlight %} + +> Note the removed brackets + +--- + +The last thing we have to change, is the `src/index.ts` file. + +{% highlight ts %} +- export { OscdMyNewPlugin } from './OscdMyNewPlugin.js'; ++ export * from './OscdMyNewPlugin.js'; +{% endhighlight %} + +> Note the asterisk on export + +--- + +This change is making sure that the plugin is default exported, so that OpenSCD can dynamically import it correctly. + +# Setting up property injection +In order to start displaying or editing the SCL file, we first need to add properties that will be injected into the plugin. + +Inside the `OscdMyNewPlugin.ts` file, add the following code: + +{% highlight ts %} +import { html, css, LitElement } from 'lit'; +import { property } from 'lit/decorators.js'; + +export default class OscdMyNewPlugin extends LitElement { + + @property({ type: Object }) + doc!: XMLDocument; + + @property({ type: Number }) + editCount = -1; + + @property({ type: String }) + locale!: string; + + ... +{% endhighlight %} + +These properties will be injected into the Plugin from the OpenSCD host. + +# Choosing the Plugin type +OpenSCD currently supports 2 different plugin types. +* Menu type plugins +* Editor type plugins + +## Menu type plugins +--- +Menu type plugins usually run on the background. They are displayed in the sidebar menu and are rendered at all times. +Example of menu type plugins are: +* Validators +* Generators + +## Editor type plugins +--- +Editor type plugins are displayed in the main Editor. An editor type plugin will be rendered when it's being activated by the Tab bar on top of OpenSCD. +Examples of editor type plugins are: +* Communication editor +* Substation editor +* Single Line Diagram + +If you choose to make your plugin a Menu type plugin, you need to implement the `run` method in your plugin. + +{% highlight ts %} +import { html, css, LitElement } from 'lit'; +import { property } from 'lit/decorators.js'; + +export default class OscdMyNewPlugin extends LitElement { + + @property({ type: Object }) + doc!: XMLDocument; + + @property({ type: Number }) + editCount = -1; + + @property({ type: String }) + locale!: string; + + async run(): Promise{ + ... Plugin implementation goes here + } + +{% endhighlight %} + +If you choose to make your plugin an Editor type plugin, you need to implement the `render` function from Lit. + +{% highlight ts %} +import { html, css, LitElement, TemplateResult } from 'lit'; +import { property } from 'lit/decorators.js'; + +export default class OscdMyNewPlugin extends LitElement { + + @property({ type: Object }) + doc!: XMLDocument; + + @property({ type: Number }) + editCount = -1; + + @property({ type: String }) + locale!: string; + + render(): TemplateResult { + return html` My plugin works!`; + } +{% endhighlight %} + +In this example, we'll choose for an editor type plugin. + +# Adding functionality to the plugin + +The SCL document can be traversed like any other HTMLElement. +Let's say you want to list all the SubStations inside the plugin. +You can do so by using + +{% highlight ts %} +Array.from(doc.querySelectorAll('Substation')).map((substation) => substation.getAttribute('name')); +{% endhighlight %} + +{% highlight ts %} +import { html, css, LitElement, TemplateResult } from 'lit'; +import { property } from 'lit/decorators.js'; + +export default class OscdMyNewPlugin extends LitElement { + + @property({ type: Object }) + doc!: XMLDocument; + + @property({ type: Number }) + editCount = -1; + + @property({ type: String }) + locale!: string; + + renderSubstationNames(): TemplateResult { + return html`+ ${Array + .from(this.doc.querySelectorAll('Substation')) + .map((substation) => substation.getAttribute('name')) + .map((name) => html`
`; + } + + render(): TemplateResult { + return html`- ${name}
`) + }${this.renderSubstationNames()}`; + } +{% endhighlight %} + +You can make the plugin as simple or difficult as you want. + + +# Building the plugin +After implementing your logic in the plugin, it's time to build the plugin. +Thankfully, the Open-WC standard provides us with npm scripts to easily build the plugin. + + +{% highlight ts %} +npm run build +{% endhighlight %} + +The script above creates the build output which can be hosted on GitHub for example. Just copy over the build directory and you're good to go. + +--- + +And that's it! You just successfully created and built a plugin that can be used with OpenSCD. + +In a different guide, we will cover more advanced things, like editing the SCL file. + +> If you're looking for the full code of this guide, [you can find it here](https://github.com/openscd/oscd-plugin-template). This repository also has templates for other frameworks. \ No newline at end of file diff --git a/collections/_guides/plugin-guide.md b/collections/_guides/plugin-guide.md new file mode 100644 index 0000000..c3a4904 --- /dev/null +++ b/collections/_guides/plugin-guide.md @@ -0,0 +1,235 @@ +--- +layout: guide +title: "How to write a plugin for OpenSCD" +name: "How to write a plugin for OpenSCD" +summary: "Brief tutorial explaining how to write a plugin for OpenSCD" +--- + + +# How to write a plugin for OpenSCD + +This tutorial will explain on how to write a plugin for OpenSCD. + +## Prerequisites +* Node ( LTS) +* Git +* An IDE + +In this example, we will use the Lit framework ([https://lit.dev](https://lit.dev)) to create a plugin for OpenSCD. On top of the Lit framework, we make use of the standards provided by Open Web Components ([open-wc.org](https://open-wc.org/)). + +# TOC +* Scaffolding a new plugin +* Setting up property injection +* Choosing the plugin type +* Adding functionality to the plugin + + +## Scaffolding a new plugin +We can scaffold a new empty project by using the `@open-wc` init command. + +```bash +npm init @open-wc +``` +--- + +When prompted, choose the following: + +`What would you like to do today?` › Scaffold a new project + +`What would you like to scaffold?` › Web Component + +`What would you like to add?` › Linting (eslint & prettier), Testing (web-test-runner), Demoing (storybook) + +`Would you like to use typescript?` › Yes + +`What is the tag name of your web component?` › oscd-my-new-plugin + +--- + +After scaffolding is complete, the first thing that we need to do is to change the plugin class. + +Open the `OscdMyNewPlugin.ts` file in the `src` folder and change the following line: + +```diff +- export class OscdMyNewPlugin extends LitElement { ++ export default class OscdMyNewPlugin extends LitElement { +``` + +> Note the `default` keyword. + +--- + +Now, the `src/oscd-my-new-plugin.ts` file needs to be changed: + +```diff +- import { OscdMyNewPlugin } from './OscdMyNewPlugin.js'; ++ import OscdMyNewPlugin from './OscdMyNewPlugin.js'; +``` + +> Note the removed brackets + +--- + +The last thing we have to change, is the `src/index.ts` file. + +```diff +- export { OscdMyNewPlugin } from './OscdMyNewPlugin.js'; ++ export * from './OscdMyNewPlugin.js'; +``` + +> Note the asterisk on export + +--- + +This change is making sure that the plugin is default exported, so that OpenSCD can dynamically import it correctly. + +## Setting up property injection +In order to start displaying or editing the SCL file, we first need to add properties that will be injected into the plugin. + +Inside the `OscdMyNewPlugin.ts` file, add the following code: + +```TypeScript +import { html, css, LitElement } from 'lit'; +import { property } from 'lit/decorators.js'; + +export default class OscdMyNewPlugin extends LitElement { + + @property({ type: Object }) + doc!: XMLDocument; + + @property({ type: Number }) + editCount = -1; + + @property({ type: String }) + locale!: string; + + ... +``` + +These properties will be injected into the Plugin from the OpenSCD host. + +## Choosing the Plugin type +OpenSCD currently supports 2 different plugin types. +* Menu type plugins +* Editor type plugins + +### Menu type plugins +--- +Menu type plugins usually run on the background. They are displayed in the sidebar menu and are rendered at all times. +Example of menu type plugins are: +* Validators +* Generators + +### Editor type plugins +--- +Editor type plugins are displayed in the main Editor. An editor type plugin will be rendered when it's being activated by the Tab bar on top of OpenSCD. +Examples of editor type plugins are: +* Communication editor +* Substation editor +* Single Line Diagram + +If you choose to make your plugin a Menu type plugin, you need to implement the `run` method in your plugin. + +```TypeScript +import { html, css, LitElement } from 'lit'; +import { property } from 'lit/decorators.js'; + +export default class OscdMyNewPlugin extends LitElement { + + @property({ type: Object }) + doc!: XMLDocument; + + @property({ type: Number }) + editCount = -1; + + @property({ type: String }) + locale!: string; + + async run(): Promise{ + ... Plugin implementation goes here + } +``` + +If you choose to make your plugin an Editor type plugin, you need to implement the `render` function from Lit. + +```TypeScript +import { html, css, LitElement, TemplateResult } from 'lit'; +import { property } from 'lit/decorators.js'; + +export default class OscdMyNewPlugin extends LitElement { + + @property({ type: Object }) + doc!: XMLDocument; + + @property({ type: Number }) + editCount = -1; + + @property({ type: String }) + locale!: string; + + render(): TemplateResult { + return html` My plugin works!`; + } +``` + +In this example, we'll choose for an editor type plugin. + +## Adding functionality to the plugin + +The SCL document can be traversed like any other HTMLElement. +Let's say you want to list all the SubStations inside the plugin. +You can do so by using + +```TypeScript +Array.from(doc.querySelectorAll('Substation')).map((substation) => substation.getAttribute('name')); +``` + +```TypeScript +import { html, css, LitElement, TemplateResult } from 'lit'; +import { property } from 'lit/decorators.js'; + +export default class OscdMyNewPlugin extends LitElement { + + @property({ type: Object }) + doc!: XMLDocument; + + @property({ type: Number }) + editCount = -1; + + @property({ type: String }) + locale!: string; + + renderSubstationNames(): TemplateResult { + return html`+ ${Array + .from(this.doc.querySelectorAll('Substation')) + .map((substation) => substation.getAttribute('name')) + .map((name) => html`
`; + } + + render(): TemplateResult { + return html`- ${name}
`) + }${this.renderSubstationNames()}`; + } +``` + +You can make the plugin as simple or difficult as you want. + + +## Building the plugin +After implementing your logic in the plugin, it's time to build the plugin. +Thankfully, the Open-WC standard provides us with npm scripts to easily build the plugin. + + +```bash +npm run build +``` +The script above creates the build output which can be hosted on GitHub for example. Just copy over the build directory and you're good to go. + +--- + +And that's it! You just successfully created and built a plugin that can be used with OpenSCD. + +In a different guide, we will cover more advanced things, like editing the SCL file. + +> If you're looking for the full code of this guide, [you can find it here](https://github.com/openscd/oscd-plugin-template). This repository also has templates for other frameworks. diff --git a/docker-compose.yml b/docker-compose.yml index 00bba59..fda14f4 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,7 +1,7 @@ services: jekyll: image: jekyll/jekyll:latest - command: jekyll serve --watch --force_polling --verbose --livereload + command: jekyll serve --watch --force_polling --verbose --livereload --trace ports: - 4000:4000 volumes: diff --git a/index.html b/index.html index a2d73ac..613b6f7 100644 --- a/index.html +++ b/index.html @@ -5,7 +5,7 @@ cover: home ---- + Get started diff --git a/pages/glossary.md b/pages/glossary.md new file mode 100644 index 0000000..9e21363 --- /dev/null +++ b/pages/glossary.md @@ -0,0 +1,40 @@ +--- +layout: default +title: Glossary +subtitle: OpenSCD Glossary +cover: solar +toc: true +--- + +# OpenSCD Glossary + +This glossary introduces terms that have been developed and collectively endorsed by the OpenSCD community. Please note that terms specific to the SCL (Substation Configuration Language) fall outside the scope of this glossary, as they are thoroughly covered and explained by the relevant IEC standards; The goal of the glossary is helping to get a common understanding of the terms introduced into the OpenSCD project, with the aim of enhancing communication. + +## Wizard +A wizard is a user-friendly interface that assists individuals in completing complicated tasks by breaking them into simpler, easy-to-follow steps. It typically offers guidance, instructions, and choices, making tasks more manageable and enabling users to accomplish them efficiently. + +## Plug-in +An OpenSCD plug-in allows to extend OpenSCD functionality. E.g. support vendeor/organisational specific needs. It's crafted to seamlessly merge with the main OpenSCD-core API, broadening its capabilities without altering its fundamental structure. OpenSCD-core incorporates a selected set of plug-ins and settings (e.g. color scheme) to enhance its functionality. + +## Distribution +An OpenSCD distirbution is openSCD-core with a set of user/organsition selected plug-ins, styling and other environmental variables. An OpenSCD distribution allows to meet organisation/user needs. E.g. selection of open source and 3rd party plug-ins or the useage of organisation colors. + +## Validator plug-in +The primary role of Validator plug-in is to ensure that the information provided meets certain criteria or requirements. In the context of our tool, this plug-in is essential for validating the IEC 61850 SCL file that's either created or modified by the tool. It acts as a quality checker, ensuring the integrity and compliance of the SCL file according to specified guidelines or standards. + +## Menu plug-in +Menu Plug-in in OpenSCD refers to the functionalities accessible directly from the main menu of the tool. These options perform specific tasks within the tool without necessarily adding a new module or plug-in to the workspace. For instance: + +- **Save Project:** Saves the current project. +- **Open Project:** Opens an existing project. +- **Create Virtual IED:** Adds a new IED (Intelligent Electronic Device). + +These options facilitate essential tasks within OpenSCD without altering the tool's interface or adding new components to the workspace. + + +## Editor plug-in +**Editor Plug-ins** in OpenSCD are accessed through the Extensions section in the Menu. These plug-ins, found in the Editor tab of the Extensions section, are responsible for adding or removing modules within the tool's workspace. For instance: **IED**, **Substation** and **Single Line Diagram** plug-ins. + +Editor Plug-ins extend the functionality of OpenSCD by adding modules or components to the workspace, providing users with additional tools and features for more specialized tasks or configurations. + + diff --git a/pages/guides.md b/pages/guides.md index 21cc979..6128771 100644 --- a/pages/guides.md +++ b/pages/guides.md @@ -5,27 +5,29 @@ subtitle: Guides cover: solar --- - - ----- Guides -
- {% for guide in site.guides %} {% endfor %}diff --git a/scripts/.gitignore b/scripts/.gitignore new file mode 100644 index 0000000..40b878d --- /dev/null +++ b/scripts/.gitignore @@ -0,0 +1 @@ +node_modules/ \ No newline at end of file diff --git a/scripts/package-lock.json b/scripts/package-lock.json new file mode 100644 index 0000000..7fc17b3 --- /dev/null +++ b/scripts/package-lock.json @@ -0,0 +1,406 @@ +{ + "name": "@openscd/scripts", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@openscd/scripts", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@octokit/rest": "^20.0.2", + "atob": "^2.1.2" + }, + "devDependencies": { + "@types/atob": "^2.1.4", + "@types/node": "^20.11.20", + "ts-node": "^10.9.2", + "typescript": "^5.3.3" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "dev": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "dev": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "dev": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@octokit/auth-token": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-4.0.0.tgz", + "integrity": "sha512-tY/msAuJo6ARbK6SPIxZrPBms3xPbfwBrulZe0Wtr/DIY9lje2HeV1uoebShn6mx7SjCHif6EjMvoREj+gZ+SA==", + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/core": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-5.1.0.tgz", + "integrity": "sha512-BDa2VAMLSh3otEiaMJ/3Y36GU4qf6GI+VivQ/P41NC6GHcdxpKlqV0ikSZ5gdQsmS3ojXeRx5vasgNTinF0Q4g==", + "dependencies": { + "@octokit/auth-token": "^4.0.0", + "@octokit/graphql": "^7.0.0", + "@octokit/request": "^8.0.2", + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0", + "before-after-hook": "^2.2.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/endpoint": { + "version": "9.0.4", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-9.0.4.tgz", + "integrity": "sha512-DWPLtr1Kz3tv8L0UvXTDP1fNwM0S+z6EJpRcvH66orY6Eld4XBMCSYsaWp4xIm61jTWxK68BrR7ibO+vSDnZqw==", + "dependencies": { + "@octokit/types": "^12.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/graphql": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-7.0.2.tgz", + "integrity": "sha512-OJ2iGMtj5Tg3s6RaXH22cJcxXRi7Y3EBqbHTBRq+PQAqfaS8f/236fUrWhfSn8P4jovyzqucxme7/vWSSZBX2Q==", + "dependencies": { + "@octokit/request": "^8.0.1", + "@octokit/types": "^12.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/openapi-types": { + "version": "19.1.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-19.1.0.tgz", + "integrity": "sha512-6G+ywGClliGQwRsjvqVYpklIfa7oRPA0vyhPQG/1Feh+B+wU0vGH1JiJ5T25d3g1JZYBHzR2qefLi9x8Gt+cpw==" + }, + "node_modules/@octokit/plugin-paginate-rest": { + "version": "9.1.5", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-9.1.5.tgz", + "integrity": "sha512-WKTQXxK+bu49qzwv4qKbMMRXej1DU2gq017euWyKVudA6MldaSSQuxtz+vGbhxV4CjxpUxjZu6rM2wfc1FiWVg==", + "dependencies": { + "@octokit/types": "^12.4.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=5" + } + }, + "node_modules/@octokit/plugin-request-log": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-4.0.0.tgz", + "integrity": "sha512-2uJI1COtYCq8Z4yNSnM231TgH50bRkheQ9+aH8TnZanB6QilOnx8RMD2qsnamSOXtDj0ilxvevf5fGsBhBBzKA==", + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=5" + } + }, + "node_modules/@octokit/plugin-rest-endpoint-methods": { + "version": "10.3.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-10.3.0.tgz", + "integrity": "sha512-c/fjpoHispRvBZuRoTVt/uALg7pXa9RQbXWJiDMk6NDkGNomuAZG7YuYYpZoxeoXv+kVRjIDTsO0e1z0pei+PQ==", + "dependencies": { + "@octokit/types": "^12.4.0" + }, + "engines": { + "node": ">= 18" + }, + "peerDependencies": { + "@octokit/core": ">=5" + } + }, + "node_modules/@octokit/request": { + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-8.2.0.tgz", + "integrity": "sha512-exPif6x5uwLqv1N1irkLG1zZNJkOtj8bZxuVHd71U5Ftuxf2wGNvAJyNBcPbPC+EBzwYEbBDdSFb8EPcjpYxPQ==", + "dependencies": { + "@octokit/endpoint": "^9.0.0", + "@octokit/request-error": "^5.0.0", + "@octokit/types": "^12.0.0", + "universal-user-agent": "^6.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/request-error": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-5.0.1.tgz", + "integrity": "sha512-X7pnyTMV7MgtGmiXBwmO6M5kIPrntOXdyKZLigNfQWSEQzVxR4a4vo49vJjTWX70mPndj8KhfT4Dx+2Ng3vnBQ==", + "dependencies": { + "@octokit/types": "^12.0.0", + "deprecation": "^2.0.0", + "once": "^1.4.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/rest": { + "version": "20.0.2", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-20.0.2.tgz", + "integrity": "sha512-Ux8NDgEraQ/DMAU1PlAohyfBBXDwhnX2j33Z1nJNziqAfHi70PuxkFYIcIt8aIAxtRE7KVuKp8lSR8pA0J5iOQ==", + "dependencies": { + "@octokit/core": "^5.0.0", + "@octokit/plugin-paginate-rest": "^9.0.0", + "@octokit/plugin-request-log": "^4.0.0", + "@octokit/plugin-rest-endpoint-methods": "^10.0.0" + }, + "engines": { + "node": ">= 18" + } + }, + "node_modules/@octokit/types": { + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-12.5.0.tgz", + "integrity": "sha512-YJEKcb0KkJlIUNU/zjnZwHEP8AoVh/OoIcP/1IyR4UHxExz7fzpe/a8IG4wBtQi7QDEqiomVLX88S6FpxxAJtg==", + "dependencies": { + "@octokit/openapi-types": "^19.1.0" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "dev": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "dev": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "dev": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "dev": true + }, + "node_modules/@types/atob": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@types/atob/-/atob-2.1.4.tgz", + "integrity": "sha512-FisOhG87cCFqzCgq6FUtSYsTMOHCB/p28zJbSN1QBo4ZGJfg9PEhMjdIV++NDeOnloUUe0Gz6jwBV+L1Ac00Mw==", + "dev": true + }, + "node_modules/@types/node": { + "version": "20.11.20", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.20.tgz", + "integrity": "sha512-7/rR21OS+fq8IyHTgtLkDK949uzsa6n8BkziAKtPVpugIkO6D+/ooXMvzXxDnZrmtXVfjb1bKQafYpb8s89LOg==", + "dev": true, + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "dev": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.2.tgz", + "integrity": "sha512-cjkyv4OtNCIeqhHrfS81QWXoCBPExR/J62oyEqepVw8WaQeSqpW2uhuLPh1m9eWhDuOo/jUXVTlifvesOWp/4A==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "dev": true + }, + "node_modules/atob": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz", + "integrity": "sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==", + "bin": { + "atob": "bin/atob.js" + }, + "engines": { + "node": ">= 4.5.0" + } + }, + "node_modules/before-after-hook": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.3.tgz", + "integrity": "sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==" + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "dev": true + }, + "node_modules/deprecation": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", + "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==" + }, + "node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "dev": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "dev": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true + }, + "node_modules/universal-user-agent": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.1.tgz", + "integrity": "sha512-yCzhz6FN2wU1NiiQRogkTQszlQSlpWaw8SvVegAc+bDxbzHgh1vX8uIe8OYyMH6DwH+sdTJsgMl36+mSMdRJIQ==" + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "dev": true + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "dev": true, + "engines": { + "node": ">=6" + } + } + } +} diff --git a/scripts/package.json b/scripts/package.json new file mode 100644 index 0000000..a34d754 --- /dev/null +++ b/scripts/package.json @@ -0,0 +1,20 @@ +{ + "name": "@openscd/scripts", + "version": "1.0.0", + "description": "", + "scripts": { + "download": "ts-node src/index.ts" + }, + "author": "OpenSCD", + "license": "ISC", + "devDependencies": { + "@types/atob": "^2.1.4", + "@types/node": "^20.11.20", + "ts-node": "^10.9.2", + "typescript": "^5.3.3" + }, + "dependencies": { + "@octokit/rest": "^20.0.2", + "atob": "^2.1.2" + } +} diff --git a/scripts/src/downloader.ts b/scripts/src/downloader.ts new file mode 100644 index 0000000..c7e37d9 --- /dev/null +++ b/scripts/src/downloader.ts @@ -0,0 +1,138 @@ +import { Octokit, RestEndpointMethodTypes } from "@octokit/rest"; + +import fs from "fs"; + +import { extname } from "path"; +import { promisify } from "util"; +import { dirname, join } from "path"; + +const mkdir = promisify(fs.mkdir); +const writeFile = promisify(fs.writeFile); + +export interface Repository { + organization: string; + repository: string; + sha?: string; +} + +export interface Filter { + extensions?: string[]; + files?: string[]; +} + +export interface GitHubOptions { + GH_TOKEN: string; + repository: Repository; + input_path: string; + filter?: Filter; + output_path?: string; +} + +export class GitHubDownloader { + private octokit: Octokit; + + constructor(private options: GitHubOptions) { + this.octokit = new Octokit({ + auth: options.GH_TOKEN, + }); + } + + downloadFolder(): Promise{ + return new Promise((resolve, reject) => { + this.getFileStructure( + this.options.input_path, + this.options.repository + ).then((files) => { + Promise.all( + files + .filter( + (node) => + node.path.startsWith(this.options.input_path) && + node.type === "file" + ) + .filter((node) => { + return this.options.filter + ? ( + this.options.filter.extensions || [extname(node.path)] + ).includes(extname(node.path)) && + (this.options.filter.files || [node.path]).includes( + node.path + ) + : true; + }) + .map(async (node) => + this.getFileContent(this.options.repository, node) + ) + ).then((files) => { + Promise.all( + files.map((file) => { + return mkdir( + dirname(join(this.options.output_path || "", file.path)), + { + recursive: true, + } + ) + .then(() => { + return writeFile( + join(this.options.output_path || "", file.path), + file.contents + ); + }) + .catch((err) => reject(err)); + }) + ) + .then(() => resolve()) + .catch((err) => reject(err)); + }); + }); + }); + } + + protected async getFileContent( + repository: Repository, + node: { sha: string; path: string } + ): Promise<{ path: string; contents: Buffer }> { + const { data } = await this.octokit.git.getBlob({ + owner: repository.organization, + repo: repository.repository, + file_sha: node.sha, + }); + + return { + path: node.path, + contents: Buffer.from(data.content, data.encoding as BufferEncoding), + }; + } + + protected getFileStructure( + path: string, + repository: Repository + ): Promise<{ path: string; type: string; sha: string }[]> { + return new Promise((resolve, reject) => { + const req: RestEndpointMethodTypes["repos"]["getContent"]["parameters"] = + { + owner: repository.organization, + repo: repository.repository, + path: path, + }; + this.octokit.repos + .getContent(req) + .then((res) => { + const dirs = (res.data as unknown[]).map((node: any) => { + if (node.type === "dir") { + return this.getFileStructure(node.path, repository); + } + return { + path: node.path, + type: node.type, + sha: node.sha, + }; + }); + Promise.all(dirs) + .then((res) => resolve(res.flat())) + .catch((err) => reject(err)); + }) + .catch((err) => reject(err)); + }); + } +} diff --git a/scripts/src/index.ts b/scripts/src/index.ts new file mode 100644 index 0000000..f74de01 --- /dev/null +++ b/scripts/src/index.ts @@ -0,0 +1,32 @@ +import { join } from "path"; +import { env } from "process"; +import { GitHubDownloader, GitHubOptions } from "./downloader"; + +const GH_TOKEN: string = process.env!.GH_TOKEN! || ""; + +const repositories: GitHubOptions[] = [ + { + repository: { + organization: "openscd", + repository: "open-scd", + }, + input_path: "docs", + GH_TOKEN: GH_TOKEN, + filter: { + extensions: [".md", ".png"], + }, + output_path: join("../", "pages", "decisions"), + }, +]; + +Promise.all( + repositories + .map((repo) => new GitHubDownloader(repo)) + .map((repo) => repo.downloadFolder()) +) + .then(() => { + console.log("Done"); + }) + .catch((err) => { + console.error(`Error: ${err}`); + }); diff --git a/scripts/tsconfig.json b/scripts/tsconfig.json new file mode 100644 index 0000000..b13029d --- /dev/null +++ b/scripts/tsconfig.json @@ -0,0 +1,14 @@ +{ + "compilerOptions": { + "target": "ESNext", + "module": "CommonJS", + "esModuleInterop": true, + "strict": true, + "lib": [ + "ESNext" + ], + "skipLibCheck": true, + "rootDir": "./src", + "outDir": "./dist" + } +} \ No newline at end of file