From 36855a017735d09996ff6934bcc3927cb72a5ba0 Mon Sep 17 00:00:00 2001 From: Zhongwei Li Date: Sun, 30 Nov 2025 08:48:07 +0800 Subject: [PATCH] Initial commit --- .claude-plugin/plugin.json | 12 + README.md | 3 + plugin.lock.json | 60 ++ skills/release-post/SKILL.md | 285 ++++++++ .../references/content-guidelines.md | 409 ++++++++++++ .../references/shiny-formatting.md | 606 ++++++++++++++++++ .../references/tidyverse-formatting.md | 275 ++++++++ .../release-post/scripts/get_contributors.R | 48 ++ 8 files changed, 1698 insertions(+) create mode 100644 .claude-plugin/plugin.json create mode 100644 README.md create mode 100644 plugin.lock.json create mode 100644 skills/release-post/SKILL.md create mode 100644 skills/release-post/references/content-guidelines.md create mode 100644 skills/release-post/references/shiny-formatting.md create mode 100644 skills/release-post/references/tidyverse-formatting.md create mode 100644 skills/release-post/scripts/get_contributors.R diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..86c8fae --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,12 @@ +{ + "name": "open-source", + "description": "Collection of skills for general open-source package development and maintenance", + "version": "0.0.0-2025.11.28", + "author": { + "name": "Garrick Aden-Buie (Posit, PBC)", + "email": "garrick@posit.co" + }, + "skills": [ + "./skills/release-post" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..7272369 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# open-source + +Collection of skills for general open-source package development and maintenance diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..1bb9fa2 --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,60 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:posit-dev/skills:open-source", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "ce081441bd7f0390c6dd3e116be8f633d88e7219", + "treeHash": "c5e09a6309d52e85e8671d6b2b58bbfa5a09897a380df94d09a58209e598fa37", + "generatedAt": "2025-11-28T10:27:39.596322Z", + "toolVersion": "publish_plugins.py@0.2.0" + }, + "origin": { + "remote": "git@github.com:zhongweili/42plugin-data.git", + "branch": "master", + "commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390", + "repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data" + }, + "manifest": { + "name": "open-source", + "description": "Collection of skills for general open-source package development and maintenance" + }, + "content": { + "files": [ + { + "path": "README.md", + "sha256": "de17cbd0c2076d3e3ac4f98e41c6e700e6726baec90503fd3c375026021a3787" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "da888cd4dd2b7c4d10e686f0facfb9ce8b0d6b9aa25568d72cc376155580a333" + }, + { + "path": "skills/release-post/SKILL.md", + "sha256": "77b4f5c981f5a41642c81fc1d84cc63a4e6397c6c8b24ffe0e681627ef86cd53" + }, + { + "path": "skills/release-post/references/content-guidelines.md", + "sha256": "b4f2960a87352918abd232d86a5a7754f903842a9d8f1e578f5f6b9657a88bae" + }, + { + "path": "skills/release-post/references/shiny-formatting.md", + "sha256": "30dd05e615eb46fa7db21d20bb02739abe0a7324a524105cf62c21627ca1e014" + }, + { + "path": "skills/release-post/references/tidyverse-formatting.md", + "sha256": "6f9d8aae3c24f9a81827b89ca3dd05f5817e2e783abdd8d02b1bbdf51f2fec10" + }, + { + "path": "skills/release-post/scripts/get_contributors.R", + "sha256": "93a03708015bf7ea7af2478004817892b5f11e65c92f934d0f73cd1481109f1b" + } + ], + "dirSha256": "c5e09a6309d52e85e8671d6b2b58bbfa5a09897a380df94d09a58209e598fa37" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file diff --git a/skills/release-post/SKILL.md b/skills/release-post/SKILL.md new file mode 100644 index 0000000..8116d86 --- /dev/null +++ b/skills/release-post/SKILL.md @@ -0,0 +1,285 @@ +--- +name: release-post +description: > + Create professional package release blog posts following Tidyverse or Shiny blog conventions. + Use when the user needs to: (1) Write a release announcement blog post for an R or Python package + for tidyverse.org or shiny.posit.co, (2) Transform NEWS/changelog content into blog format, + (3) Generate acknowledgments sections with contributor lists, (4) Format posts following specific + blog platform requirements. Supports both Tidyverse (hugodown) and Shiny (Quarto) blog formats with + automated contributor fetching and comprehensive style guidance. +--- + +# Package Release Post + +Create professional R/Python package release blog posts following Tidyverse or Shiny blog conventions. + +## Quick Start + +1. **Identify the blog platform**: Tidyverse (tidyverse.org) or Shiny (shiny.posit.co) +2. Verify NEWS.md or changelog exists for the package +3. Gather package info: name, version, repository (e.g., "tidyverse/dplyr") +4. Follow the workflow below +5. Use `scripts/get_contributors.R` to generate acknowledgments +6. Reference the appropriate formatting guide for final polish + +## Platform Selection + +This skill supports two blog platforms with different formatting requirements: + +- **Tidyverse blog** (tidyverse.org) + - Uses hugodown + - R packages primarily + - More rigid structure and conventions + - See `references/tidyverse-formatting.md` + +- **Shiny blog** (shiny.posit.co) + - Uses Quarto + - R and Python packages + - More flexible, feature-focused structure + - See `references/shiny-formatting.md` + +**First, determine which platform the post is for**, then follow the general workflow and apply platform-specific formatting. + +## General Workflow + +These steps apply to both platforms. Content guidelines are based on Tidyverse best practices but adapt them as needed for Shiny posts. + +### Step 1: Gather Information + +Collect required information: + +- **Platform**: Tidyverse or Shiny blog? +- **Package name and version**: e.g., "dplyr 1.2.0" or "shiny 1.9.0" +- **Repository**: GitHub repo in "owner/repo" format +- **Package language**: R or Python +- **NEWS content**: Read the package's NEWS.md, CHANGELOG, or NEWS +- **Package description**: One-sentence core purpose +- **Previous release tag**: For contributor fetching (optional) +- **Featured image**: For frontmatter (optional but recommended) + +### Step 2: Structure the Post + +Create the post outline following this order: + +1. **Frontmatter**: Platform-specific YAML (see formatting references) + +2. **Title and Opening**: + - Title: Package name and version + - Opening: Announcement with one-sentence package description + - Installation: Code block with installation command + - Overview: Brief summary with link to full release notes + +3. **Main Content** (choose appropriate sections): + - **Migration guide** (if breaking changes) - Always first when present + - **Lifecycle changes** (deprecations, soft-deprecations, defunct) + - **Feature sections** (one per major feature, descriptive headings) + - **Minor improvements** (bulleted list) + +4. **Acknowledgements** (when appropriate): + - Use `scripts/get_contributors.R` + - Format: "A big thank you to all the folks who helped make this release happen:" + - Comma-separated GitHub links + +### Step 3: Apply Content Guidelines + +Follow the best practices in `references/content-guidelines.md`: + +- **Opening style**: "We're [random adjective expressing excitement] to announce the release of..." +- **Section organization**: Migration → Lifecycle → Features → Improvements → Acknowledgements +- **Tone**: Conversational, professional, enthusiastic but authentic +- **Technical precision**: Use exact function names in backticks +- **Focus on benefits**: Explain "why" not just "what" +- **Code examples**: Realistic, well-commented, properly formatted + +### Step 4: Transform NEWS Content + +Convert NEWS.md bullets to blog-friendly content: + +- **Research features thoroughly**: Don't just copy NEWS bullets—read function docs, check PRs, understand the context +- **Expand context**: Why changes matter, not just what changed +- **Add complete code examples**: Show realistic usage with full workflows, not just function signatures +- **Explain concepts first**: For unfamiliar features, explain what they are and how they work before showing code +- **Group thematically**: Combine related NEWS items into coherent sections +- **Use conversational tone**: Transform terse bullets into prose +- **Link documentation**: Add relevant links to docs and resources +- **Highlight breaking changes**: Make migration paths clear +- **Multi-language parity** (Shiny only): For R+Python packages on the Shiny blog, ensure all examples show both languages in tabsets + +### Step 5: Apply Platform-Specific Formatting + +**For Tidyverse posts**, read `references/tidyverse-formatting.md` and apply: +- hugodown frontmatter with `slug`, `photo.url`, `photo.author` +- Specific slug format: `packagename-x-y-z` (hyphens replace dots) +- R code blocks with `r` language identifier +- Acknowledgements always included as final section + +**For Shiny posts**, read `references/shiny-formatting.md` and apply: +- Quarto frontmatter with YAML anchors for social media +- Flexible title formatting +- Use tabsets for Python/R or Express/Core variations +- Platform-specific code block attributes +- Acknowledgements optional, varies by post type +- May use lead paragraphs, callouts, embedded media + +### Step 6: Generate Acknowledgements + +Run the contributor script: + +```bash +Rscript scripts/get_contributors.R "owner/repo" +``` + +Or with a specific starting tag for the previous version (or tag used for last release post): + +```bash +Rscript scripts/get_contributors.R "owner/repo" "v1.0.0" +``` + +Copy the markdown output into the Acknowledgements section. + +### Step 7: Review and Polish + +Platform-agnostic checklist: + +- [ ] Frontmatter complete with all required fields +- [ ] Opening clearly states package purpose +- [ ] Installation code block present (both languages if applicable) +- [ ] Sections organized logically +- [ ] Code examples use proper syntax highlighting +- [ ] Function names in backticks with parentheses: `` `function()` `` +- [ ] Package names are not backticked or otherwise styled +- [ ] Tone is conversational but not marketing-speak +- [ ] No superlatives ("powerful", "rich", "seamless", etc.) +- [ ] Features explained with context, not just listed +- [ ] Concepts explained before showing code +- [ ] All examples show R and Python variants (if applicable) +- [ ] Links to full release notes included + +Platform-specific checklist: + +**Tidyverse:** +- [ ] Slug format: `package-x-y-z` (hyphens, not dots) +- [ ] Photo URL and author included +- [ ] Acknowledgements section is final section +- [ ] All contributors listed alphabetically + +**Shiny:** +- [ ] YAML anchors used for description (`&desc`, `*desc`) +- [ ] Social media cards configured (`open-graph`, `twitter-card`) +- [ ] Appropriate filters specified if using tabsets/shinylive +- [ ] Tabsets used for showing paired variants (Python/R, Express/Core) +- [ ] Multi-language tabsets used consistently (for R+Python packages only) + +## Reference Documentation + +Load these as needed for detailed guidance: + +### Content Guidelines +**`references/content-guidelines.md`** - General best practices for all release posts: +- Post structure and organization +- Opening style and tone +- Section hierarchy and organization +- Code examples and formatting +- Before/after patterns +- Acknowledgments conventions + +### Platform-Specific Formatting + +**`references/tidyverse-formatting.md`** - Tidyverse blog requirements: +- hugodown frontmatter structure +- Slug and title conventions +- Photo attribution +- Code block formatting +- Lifecycle section structure +- Acknowledgements format + +**`references/shiny-formatting.md`** - Shiny blog requirements: +- Quarto frontmatter with YAML anchors +- Social media card configuration +- Lead paragraphs and callouts +- Tabsets for variants +- Line highlighting and annotations +- Video embedding +- Flexible acknowledgements + +## Resources + +- **`scripts/get_contributors.R`**: Fetch formatted contributor list using `usethis::use_tidy_thanks()` +- **`references/content-guidelines.md`**: General content best practices (platform-agnostic) +- **`references/tidyverse-formatting.md`**: Tidyverse-specific formatting requirements +- **`references/shiny-formatting.md`**: Shiny-specific formatting requirements + +## Platform-Specific Quick Reference + +### Tidyverse Post Template + +````markdown +--- +output: hugodown::hugo_document +slug: package-x-y-z +title: package x.y.z +date: YYYY-MM-DD +author: Your Name +description: > + Brief description +photo: + url: https://unsplash.com/photos/id + author: Photographer Name +categories: [package] +tags: [package] +--- + +# package x.y.z + +We're pleased to announce the release of package x.y.z... + +```r +install.packages("package") +``` + +... + +## Acknowledgements + +A big thank you to all the folks who helped make this release happen: + +[Contributors from get_contributors.R] +```` + +### Shiny Post Template + +````markdown +--- +title: Package Name x.y.z +description: &desc | + Brief description of the release. +author: "Your Name" +date: "YYYY-MM-DD" + +image: feature.png + +open-graph: + image: feature.png + description: *desc +twitter-card: + image: feature.png + description: *desc +--- + +# package x.y.z + +We're excited to announce package x.y.z... + +[Installation for Python or R] + +... +```` + +## Tips + +- **Breaking changes first**: Put migration guides before features +- **Highlight the wins**: Lead with the most exciting features +- **Show don't tell**: Use code examples liberally +- **Link generously**: Help readers find more information +- **Keep it conversational**: Write like you're explaining to a colleague +- **Be authentic**: Enthusiasm should feel genuine, not marketing-speak diff --git a/skills/release-post/references/content-guidelines.md b/skills/release-post/references/content-guidelines.md new file mode 100644 index 0000000..7b390fe --- /dev/null +++ b/skills/release-post/references/content-guidelines.md @@ -0,0 +1,409 @@ +# Content Guidelines for Release Posts + +General best practices for release post content, regardless of blog platform. These guidelines are based primarily on Tidyverse blog conventions, which provide a principled approach to announcing package updates. + +## Table of Contents + +1. [Post Structure](#post-structure) +2. [Opening Style](#opening-style) +3. [Section Organization](#section-organization) +4. [Tone and Voice](#tone-and-voice) +5. [Code Examples](#code-examples) +6. [Acknowledgments](#acknowledgments) + +## Post Structure + +Standard structure for release posts: + +```markdown +[Frontmatter - see formatting guides] + +# Package Version + +[Opening paragraph with package description] + +[Installation instructions] + +[Overview paragraph about the release] + +## Major Feature 1 +[Content...] + +## Major Feature 2 +[Content...] + +## Minor improvements +[Bulleted list...] + +## Acknowledgements +[Contributor list] +``` + +## Opening Style + +### Opening Paragraph Pattern + +Start with an announcement that establishes the package's core purpose: + +> "We're [pleased/chuffed/stoked/thrilled/delighted/excited/...] to announce the release of [package] [version]. [Package] [core purpose description in one sentence]." + +**Examples:** +- "We're chuffed to announce the release of testthat 3.3.0. testthat makes it easy to turn your existing informal tests into formal, automated tests" +- "We're pleased to announce the release of pkgdown 2.2.0. pkgdown is designed to make it quick and easy to build a beautiful and accessible website" +- "We're excited to announce the new Shiny extension for VS Code!" + +**Vocabulary variations:** +- Choose an upbeat and random adjective with connotations of excitement. +- Be interesting and bold, but above all positive and fun. + +### Installation Instructions + +Follow the opening with installation instructions as a code block: + +**R packages:** +```r +install.packages("packagename") +``` + +For multiple packages: +```r +install.packages(c("shiny", "bslib")) +``` + +**Python packages:** +```bash +pip install packagename +``` + +With extras: +```bash +pip install "packagename[extra]" +``` + +### Overview Paragraph + +Brief overview of the release following installation: + +- Link to full release notes when available +- Highlight focus areas: "This release focuses on [key themes]" +- Set expectations: "This is a [major/minor] release with [X main features]" +- Acknowledge breaking changes: "This release includes a number of new features... Some of these changes may require you to update your existing code" + +## Section Organization + +### Standard Section Hierarchy + +Organize content in this order: + +1. **Migration guide** (if breaking changes) + - Put this first when there are breaking changes + - Use clear before/after examples + - Explain the rationale for changes + +2. **Lifecycle changes** (if applicable but not breaking) + - Soft deprecations + - Deprecations + - Defunct (removed) functions + - Non-breaking behavioral changes + +3. **Major new features** (primary content) + - One section per major feature or theme + - Use descriptive headings + - Lead with what the feature does and why it matters + - Include examples + +4. **Minor improvements** or **Other new features** + - Bulleted list of smaller enhancements + - Brief explanations + - Can group related items + +5. **Acknowledgements** (always final) + - Thank contributors + - List all contributors with GitHub links + +### Section Heading Style + +- Use sentence case: "Lifecycle changes" not "Lifecycle Changes" +- Be descriptive: "Easier `in_parallel()`" not just "New feature" +- Include function names in backticks when relevant +- Make headings scannable and informative + +### Content Organization Within Sections + +**For migration guides:** +```markdown +## Migrating to vX.Y.Z + +### Setting the app theme + +Prior to vX.Y.Z, you could... + +[Before example] + +**With packagename vX.Y.Z,** you now need to... + +[After example] + +Read about [the feature](#feature) below to learn why this change was needed. +``` + +**For lifecycle changes:** +```markdown +## Lifecycle changes + +* **Fully removed** after 5+ years of deprecation: + * `function1()` - use `replacement1()` instead + * `function2()` - use `replacement2()` instead + +* **Newly deprecated** (soft-deprecation): + * `function3()` is superseded by `function4()` + * `function5()` is no longer recommended + +* **Breaking changes**: + * Brief description of the change and impact +``` + +**For feature sections:** +- Lead with a clear description of what the feature does +- Provide context on why it's useful or what problem it solves +- Include code examples showing usage +- Explain configuration options if relevant +- Link to relevant documentation + +### Deep Dives on Features + +Don't just list features from NEWS—research them and provide comprehensive explanations: + +**Transform NEWS bullets into complete sections:** + +1. **Research the feature**: + - Read function documentation + - Check related PRs and issues for context + - Understand the motivation and use cases + +2. **Add complete code examples**: + - Show realistic usage, not just function signatures + - Include comments explaining what's happening + - Demonstrate the full workflow, not just isolated calls + +3. **Explain the context**: + - Why was this feature added? + - What problem does it solve? + - What are the practical use cases? + - How does it fit into typical workflows? + +4. **Show use cases and applications**: + - Provide examples of when users would need this + - Explain different configuration options + - Show how features work together + +**Example transformation:** + +❌ **NEWS bullet approach:** +```markdown +## Bookmarking +- Added `chat_restore()` for bookmarking support (#82) +``` + +✅ **Deep dive approach:** +```markdown +### Bookmarking support + +shinychat now supports Shiny's bookmarking feature for saving and restoring chat sessions. +The new `chat_restore()` function saves the chat client state and restores the message history: + +[Complete code example showing actual usage] + +By default, `chat_restore()` automatically updates the bookmark when users submit messages +and when the LLM completes its response. This means users can refresh the page or share a +URL and pick up right where they left off. + +For apps with large chat histories, you can use `enableBookmarking = "server"` to store +state server-side without URL size limitations. +``` + +**Research sources:** +- Use `r-btw` tools to read R function documentation +- Read GitHub PRs/issues linked in NEWS +- Check package vignettes and articles +- Look at example apps in the repository + +**For improvements:** +```markdown +## Minor improvements + +* Brief description of improvement with function name in backticks +* Another improvement with relevant technical details +* Performance gains quantified when possible (e.g., "2x faster") +``` + +## Tone and Voice + +### Writing Style + +- **Conversational but professional**: "We're excited to announce" rather than "It is announced" +- **Inclusive**: Use "we" and "you" rather than passive voice +- **Enthusiastic but authentic**: Avoid over-the-top marketing language +- **Technical precision**: Use exact function names, parameter names, and technical terms +- **Focus on benefits**: Explain the "why" not just the "what" + +### Avoiding Marketing Speak + +Release posts should be friendly and instructional, not advertising. Watch for and remove: + +**Superlatives and promotional adjectives:** +- ❌ "powerful set of features", "rich, interactive displays", "beautiful interface" +- ✅ "set of features", "interactive displays", "interface" + +**Exaggerated benefit claims:** +- ❌ "handles all the complexity of persisting state" +- ✅ "saves the chat client state and restores the message history" + +**Generic enthusiasm:** +- ❌ "opens up exciting possibilities for building transparent, user-friendly AI applications" +- ✅ Direct description of what the feature does + +**Over-hyped capability statements:** +- ❌ "give you fine-grained control over every aspect" +- ✅ "lets you control the chat interface" + +**Tone review checklist:** +- [ ] Remove words like "powerful", "robust", "seamless", "elegant", "revolutionary" +- [ ] Replace "makes it easy to" with direct descriptions of functionality +- [ ] Prefer descriptive statements focusing on what the function does +- [ ] Avoid overuse of phrases that sound like product marketing ("best-in-class", "production-ready") + + +### Common Phrases + +- "We're [emotion] to announce..." +- "A big thank you to all the folks who helped make this release happen" +- "You can see a full list of changes in the release notes" +- "This makes it easier to..." +- "We've improved..." +- "This release focuses on..." +- "Prior to vX.Y.Z, you had to..." +- "With this release, you can now..." + +### Technical Voice + +- Use backticks for all code elements: `` `function()` ``, `` `argument` ``, `` `"value"` `` +- Link to relevant documentation when helpful +- Be specific about behavior: "returns `NULL`" not "returns nothing" +- Explain technical decisions when they impact users +- Acknowledge tradeoffs when present + +## Code Examples + +### Explain Concepts Before Code + +For features that introduce new or unfamiliar concepts, explain the concept first before showing code: + +**Pattern: Concept → How it works → Why it matters → Code** + +1. **What is the feature/concept?** (1-2 sentences) +2. **How does it work?** (Especially non-obvious aspects) +3. **Why does it matter?** (Practical implications) +4. **Show the code** (With examples) + +**Key principles:** +- Assume readers may not be familiar with the concept +- Explain non-obvious aspects clearly +- Connect concepts to practical outcomes +- Use plain language before technical language + +### Inline Code + +- Function names: `` `map_chr()` `` +- Arguments: `` `na.rm = TRUE` `` +- Values: `` `NULL` ``, `` `TRUE` ``, `` `"string"` `` +- File paths: `` `app.py` `` + +### Code Block Principles + +- Use realistic but simple examples +- Include expected output when relevant +- Show error messages for examples of better error handling +- Format multi-line function calls with proper indentation +- Add comments for clarity when needed: `# This does X` +- Keep examples focused on the feature being demonstrated + +### Before/After Examples + +When showing improvements, use clear before/after structure: + +```markdown +Previously, you had to write: + +[Code block showing old approach] + +Now you can write: + +[Code block showing new approach] +``` + +Or for migration guides: + +```markdown +**Before:** + +[Old code] + +**After:** + +[New code] +``` + +## Acknowledgments + +### When to Include + +- **Always include** for package releases with external contributors +- Optional for internal tooling announcements +- Include at the end as the final section + +### Section Format + +```markdown +## Acknowledgements + +A big thank you to all [the folks/everyone] who [helped make this release happen/contributed to this release]: + +[@username1](https://github.com/username1), [@username2](https://github.com/username2), ... +``` + +### Fetching Contributors + +Use `usethis::use_tidy_thanks()` in R: + +```r +# From last release to current +usethis::use_tidy_thanks("owner/repo") + +# From specific tag/SHA +usethis::use_tidy_thanks("owner/repo", from = "v1.0.0") +``` + +This generates properly formatted markdown for the contributor list that can be included verbatim. + +## Transforming NEWS to Blog Content + +When converting NEWS.md entries to blog post format: + +1. **Expand context**: Technical bullets become paragraphs explaining why changes matter +2. **Add examples**: Include code demonstrating new features or changes +3. **Group thematically**: Combine scattered NEWS items into coherent sections +4. **Use conversational tone**: Transform terse bullets into readable prose +5. **Link proactively**: Add links to relevant documentation and resources +6. **Focus on user impact**: Explain how changes affect typical usage +7. **Highlight breaking changes**: Make migration paths clear and prominent + +## Release Notes Link + +Always include a link to full release notes: + +```markdown +You can see a full list of changes in the [release notes](url). +``` + +This allows readers to find exhaustive details while keeping the blog post focused and readable. diff --git a/skills/release-post/references/shiny-formatting.md b/skills/release-post/references/shiny-formatting.md new file mode 100644 index 0000000..a7ffe0a --- /dev/null +++ b/skills/release-post/references/shiny-formatting.md @@ -0,0 +1,606 @@ +# Shiny Blog Formatting Conventions + +Shiny-specific formatting requirements for blog posts on shiny.posit.co. These conventions are for the Quarto-based Shiny blog. + +## Frontmatter Format + +Shiny blog posts use Quarto YAML frontmatter with this structure: + +```yaml +--- +title: Package Name Version +description: &desc | + Brief description of the package and release. +author: "Author Name" +date: "YYYY-MM-DD" + +image: &img feature.png + +open-graph: + image: *img + description: *desc +twitter-card: + image: *img + description: *desc +--- +``` + +### Required Fields + +- **`title`**: Display title (no specific format required, flexible) + - Can be: `"packagename version"`, `"Package Name x.y.z"`, or descriptive + - Examples: + - `"shinyswatch 0.7.0"` + - `"Reintroducing the Shiny Extension for VS Code"` + - `"Branded theming for Shiny for Python apps"` +- **`description`**: Brief summary with YAML anchor for reuse + - Use `&desc` anchor and `|` for multi-line + - Reuse in social media cards with `*desc` +- **`author`**: Author name in quotes (can be single string or array) + - Single: `"Garrick Aden-Buie"` + - Multiple: `["Author One", "Author Two"]` +- **`date`**: ISO format in quotes `"YYYY-MM-DD"` +- **`image`**: Path to featured image (relative to post directory) + +### Social Media Cards + +Use YAML anchors to avoid repeating the description: + +```yaml +description: &desc | + Your description here that will be reused. + +open-graph: + description: *desc +twitter-card: + description: *desc +``` + +The `&desc` creates an anchor, and `*desc` references it. + +### Optional But Common Fields + +- **`filters`**: Quarto filters to use + - `shinylive` - For embedding Shinylive apps + - `line-highlight` - For highlighting specific lines +- **`engine`**: Quarto engine (`knitr`, `markdown`, `jupyter`) +- **`freeze`**: Set to `true` if the post includes *any* computational output +- **`format`**: HTML format options + - `code-link: true` - Enable code linking + - `anchor-sections: true` - Enable section anchors + - `reference-location: document` or `section` - Where to place footnotes +- **`code-annotations`**: Set to `hover` for hover annotations +- **`editor`**: Editor options like `render-on-save: true` + +### Image Options + +Additional image control fields: + +```yaml +image: feature.svg +imagealt: "Alternative text for image" +image-header-disable: true # Don't show image in header +``` + +### What's NOT Included + +Unlike Tidyverse posts, Shiny posts do **not** include: + +- `output: hugodown::hugo_document` +- `slug` (filename determines URL) +- `photo.url` and `photo.author` (different image structure) +- Consistent `categories` field (optional, varies by post) + +## Title Format + +The main title can be flexible: + +```markdown +# packagename version +``` + +Or more descriptive: + +```markdown +# Reintroducing the Shiny Extension for VS Code +``` + +There's less rigid formatting for titles in Shiny posts. + +## Lead Paragraphs + +Shiny posts may use a lead paragraph div for emphasis: + +```markdown +::: lead +**We're excited to announce the new Shiny extension for VS Code!** +::: +``` + +This creates a larger, emphasized opening paragraph. + +## Code Formatting + +### For Python Packages + +Use bash code blocks for installation: + +````markdown +```bash +pip install packagename +``` +```` + +With extras: + +````markdown +```bash +pip install "packagename[extra]" +``` +```` + +### For R Packages + +Use R code blocks: + +````markdown +```r +install.packages("packagename") +``` +```` + +For multiple packages: + +````markdown +```r +install.packages(c("shiny", "bslib")) +``` +```` + +### Code Block Attributes + +Shiny posts use Quarto code block attributes: + +````markdown +```{.python filename="app.py"} +from shiny import App + +# code here +``` +```` + +````markdown +```{r install-bslib} +#| eval: false +install.packages("bslib") +``` +```` + +### Line Highlighting + +Use `# <<` comments to highlight lines (requires `line-highlight` filter): + +````markdown +```{.python filename="app.py"} +from shiny.express import ui +import shinyswatch + +ui.page_opts(theme=shinyswatch.theme.darkly) # << + +# rest of code... +``` +```` + +### Code Annotations + +With `code-annotations: hover` in frontmatter, you can add annotations: + +```python +result = some_function() # <1> +``` + +```markdown +1. This annotation explains the line above +``` + +## Tabsets for Multiple Variants + +Shiny posts frequently use tabsets to show Express vs Core mode or Python vs R: + +````markdown +::: {.panel-tabset .shiny-mode-tabset group="shiny-app-mode"} +#### Express + +```{.python filename="app.py"} +from shiny.express import ui +# Express code +``` + +#### Core + +```{.python filename="app.py"} +from shiny import ui +# Core code +``` +::: +```` + +### Nested Tabsets + +You can nest tabsets for Before/After within Express/Core: + +````markdown +::: {.panel-tabset group="shiny-app-mode"} +#### Express + +::: {.panel-tabset} +##### Before + +```python +# old code +``` + +##### After + +```python +# new code +``` +::: + +#### Core + +::: {.panel-tabset} +##### Before + +```python +# old code +``` + +##### After + +```python +# new code +``` +::: +::: +```` + +## Callouts + +Quarto callouts for notes, tips, warnings: + +```markdown +::: {.callout-tip title="Writing brand.yml with the help of an LLM"} +We know that writing YAML isn't everyone's cup of tea! +... +::: +``` + +Types: `note`, `tip`, `warning`, `caution`, `important` + +## Embedded Media + +### Videos + +````markdown +::: column-page +```{=html} + +``` +::: +```` + +### Images with Attributes + +```markdown +![The task button showing a busy indication](task-button.gif){.shadow} +``` + +Or with more attributes: + +```markdown +![Alt text](image.png){fig-alt="Detailed alt text" fig-align="center" width="100%"} +``` + +## Shinylive Examples + +With the `shinylive` filter, you can embed live examples: + +````markdown +```{shinylive-python} +#| standalone: true +#| components: [editor, viewer] + +## file: app.py +from shiny.express import input, render, ui + +# App code here +``` +```` + +Or link to examples: + +```markdown +We've added a [complete branded theming example](https://shinylive.io/py/examples/#branded-theming) to shinylive.io. +``` + +## Acknowledgements Section + +Less consistent in Shiny posts. When present, variations include: + +### Variation 1: Simple Thanks + +```markdown +## Thanks! + +Thank you for trying out the Shiny extension for VS Code! +If you find it helpful, please rate the extension on [the marketplace][Shiny extension]. +``` + +### Variation 2: Multiple Package Releases + +```markdown +## Release notes + +**Big shout out to everyone involved!** 💙 +We'd want to extend a huge thank you to everyone who contributed pull requests, bug reports and feature requests. + +#### bslib [v0.7.0](https://rstudio.github.io/bslib/news/index.html#bslib-070) + +[List of contributors] + +#### shiny [v1.8.1](https://shiny.posit.co/r/reference/shiny/1.8.1/upgrade.html) + +[List of contributors] +``` + +### Variation 3: Omitted + +Some Shiny posts don't include acknowledgements, especially for announcements of tools rather than package releases. + +### When Acknowledgements Are Included + +Use similar format to Tidyverse: + +```markdown +## Acknowledgements + +We'd like to thank everyone who contributed to this release: + +[@user1](https://github.com/user1), [@user2](https://github.com/user2), and [@user3](https://github.com/user3). +``` + +Or just reference the generator in code: + +```markdown +```{r} +#| echo: false +#| eval: false +usethis::use_tidy_thanks("rstudio/bslib", from = "v0.6.1") +``` +``` + +## CSS Customization + +Shiny posts may include custom CSS in HTML blocks: + +````markdown +```{=html} + +``` +```` + +## Column Layouts + +Use Quarto's column classes for wider content: + +```markdown +::: column-page +[Wide content here] +::: + +::: column-body-outset +[Slightly wider content] +::: +``` + +## Footnotes + +Use standard markdown footnotes: + +```markdown +Text with a footnote[^footnote-key]. + +[^footnote-key]: This is the footnote text. +``` + +With `reference-location: document` or `section` in frontmatter to control placement. + +## Links and References + +Define link references at the top or bottom of the file: + +```markdown +[brand.yml]: https://posit-dev.github.io/brand-yml +[quarto]: https://quarto.org +[shiny]: https://shiny.posit.co +``` + +Then use them inline: + +```markdown +We're excited about [brand.yml] support! +``` + +## Multi-Language Support (R and Python) + +The Shiny blog often covers packages released for both R and Python (e.g., shiny, shinychat). Unlike Tidyverse posts which are language-specific, Shiny posts should show examples in both languages using Quarto tabsets. + +### Use Tabsets Consistently + +Every code example should include both R and Python variants using Quarto tabsets: + +```markdown +::: {.panel-tabset group="language"} +## R + +```r +# R code here +``` + +## Python + +```python +# Python code here +``` +::: +``` + +**Guidelines:** +- Use `group="language"` to sync all language tabsets on the page +- Provide equivalent functionality in both languages +- Don't show R-only or Python-only examples unless the feature is language-specific +- Keep examples parallel—if the R example shows 5 lines, the Python example should be similar + +### Installation Instructions + +Always show installation for both languages at the start of the post: + +```markdown +::: {.panel-tabset group="language"} +## R + +```r +install.packages("packagename") +``` + +## Python + +```bash +pip install packagename +``` +::: +``` + +### Language-Specific Features + +When features differ between languages, be explicit about the differences: + +```markdown +::: {.panel-tabset group="language"} +## R + +In R, use `tool_annotations()` to customize display: + +```r +tool_annotations(title = "My Tool", icon = bsicons::bs_icon("star")) +``` + +## Python + +In Python, use the `._display` attribute: + +```python +my_tool._display = {"title": "My Tool", "icon": "star"} +``` +::: +``` + +### Version Information + +Package versions often differ between R and Python. Be explicit: + +- "Available in shinychat for R (v0.3.0) and shinychat for Python (v0.2.0 or later)" +- Link to both language-specific documentation +- Include separate release notes links: + ```markdown + You can see the full list of changes in the [R release notes](url) and [Python release notes](url). + ``` + +### When to Use Multi-Language Tabsets + +- **Always use** for packages that have both R and Python versions (shiny, shinychat, querychat, etc.) +- **Don't use** for Python-only packages (shinyswatch) or R-only packages +- **Don't use** for Tidyverse blog posts (which are built with hugodown/Hugo, not Quarto) + +## Example Complete Frontmatter + +### Python Package + +```yaml +--- +title: shinyswatch 0.7.0 +description: &desc Customizable shinyswatch themes and an improved theme picker round out shinyswatch v0.7.0. +author: "Garrick Aden-Buie" +date: "2024-07-19" + +image: feature.jpg + +open-graph: + image: feature.jpg + description: *desc +twitter-card: + image: feature.png + description: *desc + +filters: + - line-highlight +--- +``` + +### R Package with Full Options + +```yaml +--- +title: "Shiny for R updates: Extended tasks, JavaScript errors, and many bslib improvements" +description: &desc | + An overview of recent Shiny for R updates, including extended tasks, JavaScript errors, and many bslib improvements. +author: + - Carson Sievert +date: "2024-03-27" + +image: feature.png + +open-graph: + image: feature.png + description: *desc +twitter-card: + image: feature.png + description: *desc + +editor: + render-on-save: true + +engine: knitr + +filters: + - shinylive + +freeze: true + +format: + html: + code-link: true + anchor-sections: true + reference-location: section + +code-annotations: hover +--- +``` + +## Examples of Well-Formatted Posts + +Reference these posts for formatting examples: +- [shinyswatch 0.7.0](https://shiny.posit.co/blog/posts/shinyswatch-0.7.0/) +- [Shiny Extension for VS Code 1.0.0](https://shiny.posit.co/blog/posts/shiny-vscode-1.0.0/) +- [Shiny for R 1.8.1](https://shiny.posit.co/blog/posts/shiny-r-1.8.1/) +- [Branded theming for Shiny for Python](https://shiny.posit.co/blog/posts/shiny-python-1.2-brand-yml/) diff --git a/skills/release-post/references/tidyverse-formatting.md b/skills/release-post/references/tidyverse-formatting.md new file mode 100644 index 0000000..2518a83 --- /dev/null +++ b/skills/release-post/references/tidyverse-formatting.md @@ -0,0 +1,275 @@ +# Tidyverse Blog Formatting Conventions + +Tidyverse-specific formatting requirements for blog posts on tidyverse.org. These conventions are for the hugodown-based Tidyverse blog. + +## Workflow for tidyverse.org Blog + +When creating a blog post for the official `tidyverse/tidyverse.org` repository, follow these steps: + +1. **Install hugodown** (if not already installed): + ```r + pak::pkg_install("r-lib/hugodown") + ``` + +2. **Create a new post**: + ```r + hugodown::use_tidy_post("short-name") + ``` + + This creates `content/blog/short-name/` containing an `index.Rmd` file. + + Common patterns for `"short-name"`: + - Package release: `lifecycle-1-0-0`, `parsnip-0-1-2` + - Package release with specific topic: `dplyr-1-0-0-rowwise`, `parsnip-adjacent`, `dplyr-1-0-4-if-any` + - Topic only: `self-cleaning-test-fixtures`, `taking-control-of-plot-scaling` + +3. **Write and knit**: + - Edit the generated `index.Rmd` file + - Knit `index.Rmd` to generate `index.md` + - Note: `.Rmd` files are only rendered when you explicitly knit them + +4. **Preview the site**: + ```r + hugodown::hugo_start() + ``` + This runs once per session and continues in the background to turn `.md` into `.html`. + +5. **Check for outdated files** (if concerned): + ```r + hugodown::site_outdated() + ``` + Lists all `.Rmd`s that need to be re-rendered. + +6. **Add a photo**: + Every blog post must be accompanied by a photo. If you don't have one in mind, try: + - + - + - Jenny Bryan's [free photo](https://github.com/jennybc/free-photos) link collection + +7. **Submit PR**: + - Every PR gets an automatic live preview via Netlify + - Once merged, the preview becomes the live site + +### Important Notes + +- The site uses **hugodown** (not blogdown), which separates building into two steps: + - hugodown generates `.md` from `.Rmd` + - hugo generates `.html` from `.md` +- Use `.Rmd` files (not `.Rmarkdown`) +- Output should be `output: hugodown::hugo_document` +- If updating an old post to use hugodown: + - Rename from `.Rmarkdown` to `.Rmd` + - Delete the `.markdown` file + - Set `output: hugodown::hugo_document` in YAML metadata + +### For Additional Context + +If you need more details about the workflow or encounter issues, consult the `README.md` in the `tidyverse/tidyverse.org` repository. + +## Frontmatter Format + +Tidyverse blog posts use YAML frontmatter with this structure: + +```yaml +--- +output: hugodown::hugo_document +slug: package-name-version +title: package-name version +date: YYYY-MM-DD +author: Author Name +description: > + Brief description of the package and release +photo: + url: https://unsplash.com/photos/photo-id + author: Photographer Name +categories: [package-name] +tags: [package-name, category] +--- +``` + +### Required Fields + +- **`output`**: Always `hugodown::hugo_document` +- **`slug`**: URL slug using hyphens + - Format: `packagename-x-y-z` (e.g., `ellmer-0-4-0`) + - Replace dots with hyphens in version numbers +- **`title`**: Display title with spaces + - Format: `packagename x.y.z` (e.g., `ellmer 0.4.0`) +- **`date`**: ISO format `YYYY-MM-DD` +- **`author`**: Full name of primary author +- **`description`**: Brief summary (can use `>` for multi-line) +- **`photo`**: Featured image with attribution + - `url`: Full URL to image (often Unsplash) + - `author`: Photographer name for attribution +- **`categories`**: Array with package name +- **`tags`**: Array with package name and related tags + +### Slug vs Title Convention + +The slug is used in URLs and must be URL-safe: +- Slug: `purrr-1-2-0` (hyphens, no dots) +- Title: `purrr 1.2.0` (space, dots in version) + +### Image Attribution + +Featured images are typically from Unsplash with proper photographer attribution: + +```yaml +photo: + url: https://unsplash.com/photos/abc123 + author: John Doe +``` + +## Title Format + +The main title uses a simple format with space between package name and version: + +```markdown +# packagename 1.2.0 +``` + +No "released" or "version" prefix. Just the package name and version number. + +## Code Formatting + +### Language Identifiers + +Use triple backticks with `r` language identifier for R code: + +````markdown +```r +library(packagename) + +result <- function_name( + arg1 = "value", + arg2 = TRUE +) +``` +```` + +For installation: +````markdown +```r +install.packages("packagename") +``` +```` + +### Inline Code Elements + +- Function names: `` `function()` `` +- Packages: `` `{packagename}` `` when emphasizing it's a package +- Arguments: `` `arg = value` `` +- Values: `` `NULL` ``, `` `TRUE` ``, `` `"string"` `` + +### Function Links + +When linking to function documentation, use markdown links: + +```markdown +[`function_name()`](https://url-to-docs) +``` + +## Section Structure + +### Lifecycle Section Format + +When including lifecycle changes, use this structure: + +```markdown +## Lifecycle changes + +* **Fully removed** after 5+ years of deprecation: + * `function1()` - use `replacement1()` instead + * `function2()` - use `replacement2()` instead + +* **Newly deprecated** (soft-deprecation): + * `function3()` is superseded by `function4()` + * `function5()` is no longer recommended + +* **Breaking changes**: + * Description of what changed and why +``` + +Use bold for the lifecycle stage labels. + +### Feature Sections + +Use sentence case in headings: + +```markdown +## Easier `in_parallel()` + +[Description of the feature...] +``` + +Include function names in backticks when they're part of the heading. + +## Acknowledgements Section + +Always include as the final section: + +```markdown +## Acknowledgements + +A big thank you to all the folks who helped make this release happen: + +[@username1](https://github.com/username1), [@username2](https://github.com/username2), [@username3](https://github.com/username3), and [@username4](https://github.com/username4). +``` + +### Formatting Rules + +- Single paragraph format (not bulleted list) +- GitHub handles as markdown links +- Alphabetical order +- Comma-separated with "and" before the last name +- Period at the end + +### Generating the List + +Use `usethis::use_tidy_thanks()`: + +```r +# Fetch contributors since last release +usethis::use_tidy_thanks("tidyverse/packagename") + +# Or from specific tag +usethis::use_tidy_thanks("tidyverse/packagename", from = "v1.0.0") +``` + +This function outputs properly formatted markdown that can be copied directly into the blog post. + +## Release Notes Link + +Include a link to the full release notes (typically on pkgdown site): + +```markdown +You can see a full list of changes in the [release notes](https://packagename.tidyverse.org/news/). +``` + +## Example Complete Frontmatter + +```yaml +--- +output: hugodown::hugo_document +slug: purrr-1-2-0 +title: purrr 1.2.0 +date: 2025-11-04 +author: Hadley Wickham +description: > + purrr 1.2.0 includes deprecations and minor enhancements to + functional programming tools in R. +photo: + url: https://unsplash.com/photos/xyz789 + author: Jane Photographer +categories: [purrr] +tags: [purrr, tidyverse] +--- +``` + +## Examples of Well-Formatted Posts + +Reference these posts for formatting examples: +- [pkgdown 2.2.0](https://www.tidyverse.org/blog/2025/11/pkgdown-2-2-0/) +- [testthat 3.3.0](https://www.tidyverse.org/blog/2025/11/testthat-3-3-0/) +- [purrr 1.2.0](https://www.tidyverse.org/blog/2025/11/purrr-1-2-0/) +- [ellmer 0.4.0](https://www.tidyverse.org/blog/2025/11/ellmer-0-4-0/) diff --git a/skills/release-post/scripts/get_contributors.R b/skills/release-post/scripts/get_contributors.R new file mode 100644 index 0000000..53b0808 --- /dev/null +++ b/skills/release-post/scripts/get_contributors.R @@ -0,0 +1,48 @@ +#!/usr/bin/env Rscript +# Fetch contributors for a package release using usethis::use_tidy_thanks() +# +# Usage: +# Rscript get_contributors.R [] +# +# Arguments: +# repo: GitHub repository in "owner/repo" format (e.g., "tidyverse/dplyr") +# from: Optional git ref (tag/SHA) to use as the starting point +# If omitted, uses the previous release +# +# Output: +# Markdown-formatted list of contributors suitable for blog post acknowledgments +# +# Examples: +# Rscript get_contributors.R "tidyverse/dplyr" +# Rscript get_contributors.R "tidyverse/dplyr" "v1.0.0" + +args <- commandArgs(trailingOnly = TRUE) + +if (length(args) == 0) { + cat("Error: Repository argument required\n") + cat("Usage: Rscript get_contributors.R []\n") + cat("Example: Rscript get_contributors.R 'tidyverse/dplyr'\n") + quit(status = 1) +} + +repo <- args[1] +from <- if (length(args) >= 2) args[2] else NULL + +# Check if usethis is installed +if (!requireNamespace("usethis", quietly = TRUE)) { + cat("Error: usethis package is not installed\n") + cat("Install it with: install.packages('usethis')\n") + quit(status = 1) +} + +# Fetch contributors +cat("Fetching contributors for", repo, "...\n\n") + +if (is.null(from)) { + contributors <- usethis::use_tidy_thanks(repo) +} else { + contributors <- usethis::use_tidy_thanks(repo, from = from) +} + +# The function prints the result to console +# No additional output needed