diff --git a/assets/profile-banner-dark.svg b/assets/profile-banner-dark.svg new file mode 100644 index 0000000..ac74db5 --- /dev/null +++ b/assets/profile-banner-dark.svg @@ -0,0 +1 @@ +cpp-linter: C/C++ pull requests that arrive already checked.A loop of the ways cpp-linter-action reports on a pull request: file annotations, a thread comment, the step summary, pull request reviews from clang-tidy and clang-format, and auto-fix.cpp-linterC/C++ pull requeststhat arrivealready checked.clang-format and clang-tidy, reported on the pull request.Annotationsfile-annotations1 / 6src/parser.cpp41int parse(const char *src) {42+char *end = 0;src/parser.cpp:42:15 [modernize-use-nullptr]use nullptrRun clang-format on src/parser.cppFile src/parser.cpp does not conform to Customstyle guidelines. (lines 42)Thread Commentthread-comments2 / 6github-actionscommentedCpp-Linter ReportSome files did not pass the configured checks!clang-format (v21) reports: 1 file(s) not formattedclang-tidy (v21) reports: 2 concern(s)Step Summarystep-summary3 / 6cpp-linter summaryCpp-Linter ReportSome files did not pass the configured checks!clang-format (v21) reports: 1 file(s) not formattedclang-tidy (v21) reports: 2 concern(s)src/parser.cpp:42:15: warning: [modernize-use-nullptr]use nullptrJob summary generated at run-timePull Request Reviewtidy-review4 / 6github-actionsreviewedclang-tidy diagnosticsuse nullptr [modernize-use-nullptr]Suggested change-char *end = 0;+char *end = nullptr;Commit suggestionPull Request Reviewformat-review5 / 6github-actionsreviewedclang-format suggestionSuggested change-if(ok){return 1;}+if (ok) {+return 1;+}Commit suggestionAuto-fixauto-fix6 / 6Commitsfix: handle an empty inputyoustyle: apply clang-format fixesauto-fix · 2 files changedclang-format -i runs on the files with style issues,and the result is pushed to the pull request branch. diff --git a/assets/profile-banner-light.svg b/assets/profile-banner-light.svg new file mode 100644 index 0000000..c04f57c --- /dev/null +++ b/assets/profile-banner-light.svg @@ -0,0 +1 @@ +cpp-linter: C/C++ pull requests that arrive already checked.A loop of the ways cpp-linter-action reports on a pull request: file annotations, a thread comment, the step summary, pull request reviews from clang-tidy and clang-format, and auto-fix.cpp-linterC/C++ pull requeststhat arrivealready checked.clang-format and clang-tidy, reported on the pull request.Annotationsfile-annotations1 / 6src/parser.cpp41int parse(const char *src) {42+char *end = 0;src/parser.cpp:42:15 [modernize-use-nullptr]use nullptrRun clang-format on src/parser.cppFile src/parser.cpp does not conform to Customstyle guidelines. (lines 42)Thread Commentthread-comments2 / 6github-actionscommentedCpp-Linter ReportSome files did not pass the configured checks!clang-format (v21) reports: 1 file(s) not formattedclang-tidy (v21) reports: 2 concern(s)Step Summarystep-summary3 / 6cpp-linter summaryCpp-Linter ReportSome files did not pass the configured checks!clang-format (v21) reports: 1 file(s) not formattedclang-tidy (v21) reports: 2 concern(s)src/parser.cpp:42:15: warning: [modernize-use-nullptr]use nullptrJob summary generated at run-timePull Request Reviewtidy-review4 / 6github-actionsreviewedclang-tidy diagnosticsuse nullptr [modernize-use-nullptr]Suggested change-char *end = 0;+char *end = nullptr;Commit suggestionPull Request Reviewformat-review5 / 6github-actionsreviewedclang-format suggestionSuggested change-if(ok){return 1;}+if (ok) {+return 1;+}Commit suggestionAuto-fixauto-fix6 / 6Commitsfix: handle an empty inputyoustyle: apply clang-format fixesauto-fix · 2 files changedclang-format -i runs on the files with style issues,and the result is pushed to the pull request branch. diff --git a/assets/readme-banner-small.png b/assets/readme-banner-small.png deleted file mode 100644 index 74eb73c..0000000 Binary files a/assets/readme-banner-small.png and /dev/null differ diff --git a/assets/readme-banner.png b/assets/readme-banner.png deleted file mode 100644 index fa7a1da..0000000 Binary files a/assets/readme-banner.png and /dev/null differ diff --git a/branding/README.md b/branding/README.md index 11e2df2..eed8acb 100644 --- a/branding/README.md +++ b/branding/README.md @@ -24,6 +24,7 @@ Ink `#26241F` · Paper `#FAF6EE`. | Asset | File | Use | |-------|------|-----| | README hero banner | `social/org-readme-banner.png` | `.github/profile/README.md`, repo READMEs | +| Profile banner (animated) | `../assets/profile-banner-light.svg`, `../assets/profile-banner-dark.svg` | `profile/README.md`, light and dark themes; see [Profile banner](#profile-banner) | | Avatar — light | `social/avatar-light.png` | Org/profile avatar (640×640) | | Avatar — dark | `social/avatar-dark.png` | Dark backgrounds | | Avatar — gold tint | `social/avatar-tint.png` | Accent / alt avatar | @@ -34,6 +35,16 @@ Ink `#26241F` · Paper `#FAF6EE`. Browse everything live in **Brand Assets.dc.html**. +## Profile banner + +`profile-banner/generate.py` draws both banners. It embeds `profile-banner/logo-72.png` and the fonts in +`profile-banner/fonts`, cut down to the characters the banner uses (SIL Open Font License, texts alongside). +After changing the copy or the colors, run: + +```bash +uv run branding/profile-banner/generate.py +``` + ## Developer Notes The *.xcf files in this folder are GIMP projects. diff --git a/branding/profile-banner/fonts/OFL-bricolage-grotesque.txt b/branding/profile-banner/fonts/OFL-bricolage-grotesque.txt new file mode 100644 index 0000000..4dfd4fe --- /dev/null +++ b/branding/profile-banner/fonts/OFL-bricolage-grotesque.txt @@ -0,0 +1,93 @@ +Copyright 2022 The Bricolage Grotesque Project Authors (https://github.com/ateliertriay/bricolage) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/branding/profile-banner/fonts/OFL-instrument-sans.txt b/branding/profile-banner/fonts/OFL-instrument-sans.txt new file mode 100644 index 0000000..d41633c --- /dev/null +++ b/branding/profile-banner/fonts/OFL-instrument-sans.txt @@ -0,0 +1,93 @@ +Copyright 2022 The Instrument Sans Project Authors (https://github.com/Instrument/instrument-sans) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/branding/profile-banner/fonts/OFL-jetbrains-mono.txt b/branding/profile-banner/fonts/OFL-jetbrains-mono.txt new file mode 100644 index 0000000..821a3da --- /dev/null +++ b/branding/profile-banner/fonts/OFL-jetbrains-mono.txt @@ -0,0 +1,93 @@ +Copyright 2020 The JetBrains Mono Project Authors (https://github.com/JetBrains/JetBrainsMono) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. + +This license is copied below, and is also available with a FAQ at: https://scripts.sil.org/OFL + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/branding/profile-banner/fonts/bricolage-grotesque-latin.woff2 b/branding/profile-banner/fonts/bricolage-grotesque-latin.woff2 new file mode 100644 index 0000000..b908aff Binary files /dev/null and b/branding/profile-banner/fonts/bricolage-grotesque-latin.woff2 differ diff --git a/branding/profile-banner/fonts/instrument-sans-latin.woff2 b/branding/profile-banner/fonts/instrument-sans-latin.woff2 new file mode 100644 index 0000000..665fa65 Binary files /dev/null and b/branding/profile-banner/fonts/instrument-sans-latin.woff2 differ diff --git a/branding/profile-banner/fonts/jetbrains-mono-latin.woff2 b/branding/profile-banner/fonts/jetbrains-mono-latin.woff2 new file mode 100644 index 0000000..2ca6ac6 Binary files /dev/null and b/branding/profile-banner/fonts/jetbrains-mono-latin.woff2 differ diff --git a/branding/profile-banner/generate.py b/branding/profile-banner/generate.py new file mode 100644 index 0000000..7a63d0b --- /dev/null +++ b/branding/profile-banner/generate.py @@ -0,0 +1,505 @@ +# /// script +# requires-python = ">=3.9" +# dependencies = ["fonttools[woff]"] +# /// +"""Generate the animated banner of the organization profile README. + +Writes assets/profile-banner-light.svg and assets/profile-banner-dark.svg. +Run from anywhere in the repository: + + uv run branding/profile-banner/generate.py + +The fonts in ./fonts are subset to the characters the banner uses and embedded, +together with ./logo-72.png (branding/logo.png scaled to 72 px), so the SVG files +load nothing else. Edit the copy in the scene functions below and run it again. +""" + +import base64 +import html +import io +from pathlib import Path + +from fontTools import subset +from fontTools.ttLib import TTFont +from fontTools.varLib import instancer + +HERE = Path(__file__).resolve().parent +FONTS = HERE / "fonts" +ASSETS = HERE.parent.parent / "assets" + +W, H = 880, 280 +SCENE_SECONDS = 3.5 + +FONT_FILES = { + "b": "bricolage-grotesque-latin.woff2", + "s": "instrument-sans-latin.woff2", + "m": "jetbrains-mono-latin.woff2", +} +FAMILY = {"b": "Bricolage Grotesque", "s": "Instrument Sans", "m": "JetBrains Mono"} +WEIGHTS = {"b": "700", "s": "400 700", "m": "400 800"} + +# ---------------------------------------------------------------- measuring + +_instances = {} + + +def _instance(ff, weight, opsz=None): + key = (ff, weight, opsz) + if key not in _instances: + font = TTFont(FONTS / FONT_FILES[ff]) + location = {"wght": weight} + if ff == "b": + location["opsz"] = opsz or 36 + _instances[key] = instancer.instantiateVariableFont(font, location) + return _instances[key] + + +def measure(text, ff, size, weight=400, ls=0.0): + """Advance width of `text` in px, without kerning.""" + font = _instance(ff, weight, size if ff == "b" else None) + cmap = font.getBestCmap() + hmtx = font["hmtx"] + upem = font["head"].unitsPerEm + width = 0 + for ch in text: + glyph = cmap.get(ord(ch)) + width += hmtx[glyph][0] if glyph else upem * 0.5 + return width * size / upem + ls * size * len(text) + + +USED = {"b": set(), "s": set(), "m": set()} + +# ---------------------------------------------------------------- palette + +LIGHT = dict( + page="#FFFFFF", card="#F7F5EF", cardStroke="#E4DFD1", ink="#171A2E", muted="#5A5E70", + faint="#7D8092", yellow="#FFC20A", green="#40B385", indigo="#4051B5", panel="#FFFFFF", + panelStroke="#E4DFD1", strip="#F4F1E8", addBg="#E1F3E9", addText="#1F5E43", delBg="#FBE6E3", + delText="#8A2A1C", warnBg="#FFF4D6", warnIcon="#B7791F", noteBg="#ECEFFB", noteIcon="#4051B5", + track="#E4DFD1", fill="#171A2E", shadow="#171A2E", shadowOpacity="0.16", avatar="#171A2E", + button="#4051B5", highlight="#E1F3E9", line="#E4DFD1", cursorFill="#FFFFFF", cursorStroke="#171A2E", +) +DARK = dict( + page="#0D1117", card="#16182A", cardStroke="#2A2E48", ink="#F4F2EC", muted="#A9AEC8", + faint="#8A8FAD", yellow="#FFC20A", green="#40B385", indigo="#8E9CF5", panel="#1E2136", + panelStroke="#353A5C", strip="#262A45", addBg="#173628", addText="#A3E2C2", delBg="#3A1F25", + delText="#F4B9AF", warnBg="#3A3220", warnIcon="#F2C14E", noteBg="#262B4D", noteIcon="#8E9CF5", + track="#353A5C", fill="#F4F2EC", shadow="#000000", shadowOpacity="0.45", avatar="#8E9CF5", + button="#5566D1", highlight="#173628", line="#353A5C", cursorFill="#FFFFFF", cursorStroke="#16182A", +) + +# ---------------------------------------------------------------- primitives + + +def esc(s): + return html.escape(s, quote=True) + + +def text(x, y, s, ff, size, fill, weight=400, anchor=None, ls=None, cls=None): + # SVG collapses leading spaces, so indent code by position instead + indent = len(s) - len(s.lstrip(" ")) + if indent and ff == "m": + x += indent * measure(" ", "m", size) + s = s.lstrip(" ") + USED[ff].update(s) + attrs = [f'x="{x:g}"', f'y="{y:g}"', f'class="ff-{ff}{(" " + cls) if cls else ""}"', + f'font-size="{size:g}"', f'font-weight="{weight}"', f'fill="{fill}"'] + if anchor: + attrs.append(f'text-anchor="{anchor}"') + if ls: + attrs.append(f'letter-spacing="{ls * size:.2f}"') + return f'{esc(s)}' + + +def rich(x, y, parts, ff, size, fill): + """parts: list of (string, weight, fill-or-None)""" + spans = [] + for s, weight, color in parts: + USED[ff].update(s) + spans.append(f'{esc(s)}') + return f'{"".join(spans)}' + + +def rect(x, y, w, h, fill="none", rx=0, stroke=None, extra=""): + s = f'" + + +def g(inner, cls=None, extra=""): + return f'{"".join(inner)}' + + +# ---------------------------------------------------------------- layout + +PX, PY = 40, 34 # card padding +RX0, RW = 480, 360 # right column +PANEL = (RX0, 70, RW, 176) # x, y, w, h +IX = RX0 + 14 # panel inner x + + +def avatar(c, cx, cy): + return (f'' + f'') + + +def author_row(c, y, verb): + name = "github-actions" + nw = measure(name, "s", 12, 600) + return [avatar(c, IX + 9, y - 4), + text(IX + 24, y, name, "s", 12, c["ink"], 600), + text(IX + 24 + nw + 6, y, verb, "s", 12, c["muted"])] + + +def warn_icon(c, cx, cy): + return (f'' + f'' + f'') + + +def info_icon(c, cx, cy): + return (f'' + f'' + f'') + + +def caret(c, x, cy): + return f'' + + +def check_circle(c, cx, cy, r=8): + return (f'' + f'') + + +# Scene titles are the headings of the cpp-linter-action README, and each chip is +# the action input that turns the feature on. The strings inside the scenes match +# what cpp-linter posts: annotation titles and messages (rest_api/github_api.py), +# the report used by the thread comment and the step summary (rest_api/__init__.py), +# the review headings (clang_tools/*.py) and the default auto-fix commit message. + + +def scene_notes(c): + x, y, w, h = PANEL + out = [rect(x, y, w, 26, c["strip"]), + text(IX, y + 17, "src/parser.cpp", "m", 11, c["muted"]), + text(IX, y + 43, "41", "m", 11, c["faint"]), + text(IX + 36, y + 43, "int parse(const char *src) {", "m", 11.5, c["ink"]), + rect(x, y + 50, w, 19, c["addBg"]), + text(IX, y + 63, "42", "m", 11, c["addText"]), + text(IX + 22, y + 63, "+", "m", 11.5, c["addText"]), + text(IX + 36, y + 63, " char *end = 0;", "m", 11.5, c["addText"])] + a1y = y + 76 + out.append(g([rect(x + 10, a1y, w - 20, 38, c["warnBg"], rx=6), + warn_icon(c, x + 24, a1y + 14), + text(x + 38, a1y + 16, "src/parser.cpp:42:15 [modernize-use-nullptr]", "s", 11, c["ink"], 600), + text(x + 38, a1y + 31, "use nullptr", "s", 11, c["muted"])], cls="cl-a1")) + a2y = a1y + 44 + out.append(g([rect(x + 10, a2y, w - 20, 52, c["noteBg"], rx=6), + info_icon(c, x + 24, a2y + 13), + text(x + 38, a2y + 16, "Run clang-format on src/parser.cpp", "s", 11, c["ink"], 600), + text(x + 38, a2y + 31, "File src/parser.cpp does not conform to Custom", "s", 11, c["muted"]), + text(x + 38, a2y + 45, "style guidelines. (lines 42)", "s", 11, c["muted"])], cls="cl-a2")) + return out + + +def scene_comment(c): + x, y, w, h = PANEL + out = author_row(c, y + 22, "commented") + out.append(f'') + out.append(text(IX, y + 58, "Cpp-Linter Report", "s", 16, c["ink"], 600)) + out.append(warn_icon(c, IX + 6, y + 75)) + out.append(text(IX + 18, y + 79, "Some files did not pass the configured checks!", "s", 11.5, c["ink"])) + r1 = y + 90 + out.append(g([rect(x + 10, r1, w - 20, 30, c["strip"], rx=6), caret(c, IX + 2, r1 + 15), + rich(IX + 14, r1 + 19, [("clang-format (v21) reports: ", 400, None), + ("1 file(s) not formatted", 600, None)], "s", 11.5, c["ink"])], cls="cl-r1")) + r2 = r1 + 36 + out.append(g([rect(x + 10, r2, w - 20, 30, c["strip"], rx=6), caret(c, IX + 2, r2 + 15), + rich(IX + 14, r2 + 19, [("clang-tidy (v21) reports: ", 400, None), + ("2 concern(s)", 600, None)], "s", 11.5, c["ink"])], cls="cl-r2")) + return out + + +def scene_summary(c): + x, y, w, h = PANEL + out = [rect(x, y, w, 26, c["strip"]), + text(IX, y + 18, "cpp-linter summary", "s", 12, c["ink"], 600), + text(IX, y + 48, "Cpp-Linter Report", "s", 16, c["ink"], 600), + warn_icon(c, IX + 6, y + 62), + text(IX + 18, y + 66, "Some files did not pass the configured checks!", "s", 11.5, c["ink"]), + caret(c, IX + 2, y + 82), + rich(IX + 14, y + 86, [("clang-format (v21) reports: ", 400, None), + ("1 file(s) not formatted", 600, None)], "s", 11.5, c["ink"]), + f'', + rich(IX + 14, y + 105, [("clang-tidy (v21) reports: ", 400, None), + ("2 concern(s)", 600, None)], "s", 11.5, c["ink"])] + out.append(g([f'', + rich(IX + 24, y + 124, [("src/parser.cpp:42:15:", 600, None), + (" warning: [modernize-use-nullptr]", 400, None)], "s", 11, c["ink"]), + rect(IX + 24, y + 130, 2.5, 14, c["line"]), + text(IX + 32, y + 141, "use nullptr", "s", 11, c["muted"])], cls="cl-sd")) + out.append(text(IX, y + 166, "Job summary generated at run-time", "s", 10.5, c["muted"])) + return out + + +def scene_tidy_review(c): + x, y, w, h = PANEL + out = author_row(c, y + 22, "reviewed") + out.append(text(IX, y + 46, "clang-tidy diagnostics", "s", 13, c["ink"], 600)) + out.append(f'') + out.append(rich(IX + 12, y + 64, [("use nullptr [", 400, None), ("modernize-use-nullptr", 400, c["indigo"]), + ("]", 400, None)], "s", 11.5, c["ink"])) + bx, by, bw = x + 10, y + 74, w - 20 + rows = [("-", " char *end = 0;", c["delBg"], c["delText"]), + ("+", " char *end = nullptr;", c["addBg"], c["addText"])] + box = [rect(bx, by, bw, 20, c["strip"]), + text(bx + 10, by + 14, "Suggested change", "s", 10.5, c["muted"])] + for i, (sign, code, bg, fg) in enumerate(rows): + ry = by + 20 + i * 17 + box += [rect(bx, ry, bw, 17, bg), text(bx + 10, ry + 12.5, sign, "m", 11.5, fg), + text(bx + 26, ry + 12.5, code, "m", 11.5, fg)] + hb = 20 + len(rows) * 17 + out.append(f'') + out.append(g(box, extra=f'clip-path="url(#{c["_id"]}-tidy)"')) + out.append(rect(bx + 0.5, by + 0.5, bw - 1, hb - 1, "none", rx=6, stroke=c["panelStroke"])) + label = "Commit suggestion" + btw = measure(label, "s", 11, 600) + 22 + btx, bty = x + w - 10 - btw, by + hb + 8 + out.append(rect(btx, bty, btw, 22, c["button"], rx=6)) + out.append(text(btx + btw / 2, bty + 15, label, "s", 11, "#FFFFFF", 600, anchor="middle")) + return out + + +def scene_format_review(c): + x, y, w, h = PANEL + out = author_row(c, y + 22, "reviewed") + out.append(text(IX, y + 46, "clang-format suggestion", "s", 13, c["ink"], 600)) + bx, by, bw = x + 10, y + 54, w - 20 + rows = [("-", "if(ok){return 1;}", c["delBg"], c["delText"]), + ("+", "if (ok) {", c["addBg"], c["addText"]), + ("+", " return 1;", c["addBg"], c["addText"]), + ("+", "}", c["addBg"], c["addText"])] + box = [rect(bx, by, bw, 20, c["strip"]), + text(bx + 10, by + 14, "Suggested change", "s", 10.5, c["muted"])] + for i, (sign, code, bg, fg) in enumerate(rows): + ry = by + 20 + i * 17 + box += [rect(bx, ry, bw, 17, bg), text(bx + 10, ry + 12.5, sign, "m", 11.5, fg), + text(bx + 26, ry + 12.5, code, "m", 11.5, fg)] + out.append(f'') + out.append(g(box, extra=f'clip-path="url(#{c["_id"]}-sug)"')) + out.append(rect(bx + 0.5, by + 0.5, bw - 1, 20 + 4 * 17 - 1, "none", rx=6, stroke=c["panelStroke"])) + label = "Commit suggestion" + lw = measure(label, "s", 11, 600) + btw, bth = lw + 22, 22 + btx, bty = x + w - 10 - btw, by + 20 + 4 * 17 + 8 + out.append(g([rect(btx, bty, btw, bth, c["button"], rx=6), + text(btx + btw / 2, bty + 15, label, "s", 11, "#FFFFFF", 600, anchor="middle")], cls="cl-btn")) + # pointer that clicks the button + cx, cy = btx + btw * 0.62, bty + bth * 0.55 + out.append(g([f''], + cls="cl-cursor", extra=f'transform="translate({cx:.1f} {cy:.1f})"')) + return out + + +def scene_autofix(c): + x, y, w, h = PANEL + out = [text(IX, y + 24, "Commits", "s", 12, c["muted"], 600), + f'', + f'', + text(IX + 22, y + 52, "fix: handle an empty input", "s", 12, c["ink"]), + text(x + w - 14, y + 52, "you", "s", 11, c["muted"], anchor="end")] + hy = y + 66 + out.append(g([rect(x + 8, hy, w - 16, 50, c["highlight"], rx=8), + check_circle(c, IX + 7, hy + 18), + text(IX + 22, hy + 22, "style: apply clang-format fixes", "s", 12, c["ink"], 600), + text(IX + 22, hy + 39, "auto-fix · 2 files changed", "s", 11, c["muted"])], cls="cl-c2")) + out.append(text(IX, y + 146, "clang-format -i runs on the files with style issues,", "s", 11, c["muted"])) + out.append(text(IX, y + 161, "and the result is pushed to the pull request branch.", "s", 11, c["muted"])) + return out + + +# (title, action input, drawing) +SCENES = [ + ("Annotations", "file-annotations", scene_notes), + ("Thread Comment", "thread-comments", scene_comment), + ("Step Summary", "step-summary", scene_summary), + ("Pull Request Review", "tidy-review", scene_tidy_review), + ("Pull Request Review", "format-review", scene_format_review), + ("Auto-fix", "auto-fix", scene_autofix), +] +N = len(SCENES) +CYCLE = SCENE_SECONDS * N +STILL = 3 # the scene shown without animation (tidy-review) + + +def banner_body(c, logo_markup): + """Everything inside except
- - cpp-linter brand logo - - ### Lint and format your C/C++ code in every workflow — GitHub Actions, pre-commit hooks, and CLI. - - [![Docs](https://img.shields.io/badge/Docs-cpp--linter.github.io-blue?logo=readthedocs&logoColor=white)](https://cpp-linter.github.io/) - [![GitHub Action](https://img.shields.io/badge/GitHub_Action-cpp--linter--action-2088FF?logo=githubactions&logoColor=white)](https://github.com/cpp-linter/cpp-linter-action) - [![pre-commit](https://img.shields.io/badge/pre‑commit-cpp--linter--hooks-F7B93E?logo=pre-commit&logoColor=white)](https://github.com/cpp-linter/cpp-linter-hooks) - [![CLI](https://img.shields.io/badge/CLI-cpp--linter-00ADD8?logo=python&logoColor=white)](https://github.com/cpp-linter/cpp-linter) - [![License](https://img.shields.io/badge/License-MIT-green.svg?logo=opensourceinitiative&logoColor=white)](https://github.com/cpp-linter/.github/blob/main/LICENSE) - [![Sponsor me on Open Collective](https://img.shields.io/badge/Open--Collective-Sponsors-EA4AAA?style=flat&logo=opencollective "Sponsor me on Open Collective")](https://opencollective.com/cpp-linter) - -
- ---- - -## 📖 About - -The **cpp-linter** organization delivers **clang-format**, **clang-tidy**, and other LLVM tools as ready-to-use packages across **GitHub Actions, pre-commit hooks, CLI, Docker, and more** — no building from source. - ---- - -## 🧭 Which one should I use? - -| Your goal | Start here | One-liner | -|-----------|------------|-----------| -| **CI/CD** linting | [cpp-linter-action](https://github.com/cpp-linter/cpp-linter-action) | Add to `.github/workflows/` | -| **Pre-commit** linting | [cpp-linter-hooks](https://github.com/cpp-linter/cpp-linter-hooks) | Add to `.pre-commit-config.yaml` | -| **Cross-platform** CLI | [clang-tools-pip](https://github.com/cpp-linter/clang-tools-pip) | `pip install clang-tools` | -| **macOS** native | [homebrew-tap](https://github.com/cpp-linter/homebrew-tap) | `brew install cpp-linter/tap/clang-tools` | -| **Version** management | [asdf-clang-tools](https://github.com/cpp-linter/asdf-clang-tools) | `asdf plugin add clang-format ...` | -| **Docker** images | [clang-tools-docker](https://github.com/cpp-linter/clang-tools-docker) | `docker pull ...` | -| **Static** binaries | [clang-tools-static-binaries](https://github.com/cpp-linter/clang-tools-static-binaries) | Download from [releases](https://github.com/cpp-linter/clang-tools-static-binaries/releases) | -> 📖 Browse every repository on the [organization page →](https://github.com/cpp-linter) - ---- - -## 👥 Maintainers - - - - - - -
- - shenxianpeng
- shenxianpeng -
-
- - 2bndy5
- 2bndy5 -
-
- ---- - -## 🤝 Contributing - -We welcome contributions of all kinds — bug reports, feature requests, documentation improvements, and code. - -- 📋 Read our [Code of Conduct](https://github.com/cpp-linter/.github/blob/main/CODE_OF_CONDUCT.md) -- 🔧 Check individual repos for their `CONTRIBUTING.md` guides -- 💬 Join the discussion on [GitHub Discussions](https://github.com/orgs/cpp-linter/discussions) - ---- - -

- Made with ❤️ by the cpp-linter community -

+ + + + + cpp-linter: C/C++ pull requests that arrive already checked. + + +cpp-linter runs clang-format and clang-tidy on C and C++ code: on every pull request, +before every commit and on your laptop, at the same LLVM version. + +[Website](https://cpp-linter.github.io/) · +[Get started](https://cpp-linter.github.io/getting-started/) · +[Showcase](https://cpp-linter.github.io/showcase/) · +[Discussions](https://github.com/orgs/cpp-linter/discussions) · +[Sponsor](https://opencollective.com/cpp-linter) + +### Pick where the checks run + +| Where | Project | Start with | +| :-- | :-- | :-- | +| On every pull request | [cpp-linter-action](https://github.com/cpp-linter/cpp-linter-action) | `uses: cpp-linter/cpp-linter-action@v2` | +| Before every commit | [cpp-linter-hooks](https://github.com/cpp-linter/cpp-linter-hooks) | `args: [--style=file, --version=21]` | +| Locally or in other CI | [cpp-linter](https://github.com/cpp-linter/cpp-linter) | `pip install cpp-linter` | +| Just the clang tools | [clang-tools](https://github.com/cpp-linter/clang-tools-pip) | `pip install clang-tools` | + +The clang tools also come as [static binaries](https://github.com/cpp-linter/clang-tools-static-binaries) +for Linux, macOS and Windows, a [Homebrew tap](https://github.com/cpp-linter/homebrew-tap) for macOS, +an [asdf plugin](https://github.com/cpp-linter/asdf-clang-tools) and +[Docker images](https://github.com/cpp-linter/clang-tools-docker). + +Maintained by two volunteers, [@shenxianpeng](https://github.com/shenxianpeng) and +[@2bndy5](https://github.com/2bndy5). Sponsorship goes to the project through +[Open Collective](https://opencollective.com/cpp-linter), where all income and expenses are public.