Initial commit

This commit is contained in:
Zhongwei Li
2025-11-30 08:48:07 +08:00
commit 36855a0177
8 changed files with 1698 additions and 0 deletions

View File

@@ -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"
]
}

3
README.md Normal file
View File

@@ -0,0 +1,3 @@
# open-source
Collection of skills for general open-source package development and maintenance

60
plugin.lock.json Normal file
View File

@@ -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": []
}
}

View File

@@ -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

View File

@@ -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.

View File

@@ -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}
<video controls>
<source src="videos/demo.webm" type="video/webm">
<source src="videos/demo.mp4" type="video/mp4">
</video>
```
:::
````
### 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}
<style>
img { border-radius: 8px; }
video {
max-width: 100%;
margin-bottom: 1rem;
}
</style>
```
````
## 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/)

View File

@@ -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:
- <https://unsplash.com>
- <https://pexels.com>
- 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/)

View File

@@ -0,0 +1,48 @@
#!/usr/bin/env Rscript
# Fetch contributors for a package release using usethis::use_tidy_thanks()
#
# Usage:
# Rscript get_contributors.R <repo> [<from>]
#
# 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 <repo> [<from>]\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