Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 31 additions & 0 deletions docs/widgets.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ Shipyard has support for displaying dynamic content in the form of widgets. Ther
- [Stock Price History](#stock-price-history)
- [ETH Gas Prices](#eth-gas-prices)
- [Joke of the Day](#joke)
- [Chuck Norris quotes](#chucknorris)
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix the broken table-of-contents anchor.

The link points to #chucknorris, but the markdown heading renders as #chuck-norris-quotes, so the TOC entry won’t jump to the section. Update the fragment to use #chuck-norris-quotes.

🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

32-32: Link fragments should be valid

(MD051, link-fragments)

🤖 Prompt for AI Agents
In docs/widgets.md around line 32, the TOC entry uses the wrong fragment
'#chucknorris' which doesn't match the rendered heading; update the link target
to '#chuck-norris-quotes' so the table-of-contents anchor matches the heading
and navigates correctly.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In docs/widgets.md around line 32, the TOC entry uses the wrong fragment
'#chucknorris' which doesn't match the rendered heading; update the link target
to '#chuck-norris-quotes' so the table-of-contents anchor matches the heading
and navigates correctly.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@khulnasoft-bot, you're absolutely right! The table of contents anchor needs to be corrected.

The issue is that markdown heading anchors are generated by converting the heading text to lowercase and replacing spaces with hyphens. So the heading ### Chuck Norris quotes creates the anchor #chuck-norris-quotes, not #chucknorris.

The fix:

Change line 32 from:

  - [Chuck Norris quotes](#chucknorris)

To:

  - [Chuck Norris quotes](#chuck-norris-quotes)

This will ensure the table of contents link correctly navigates to the Chuck Norris quotes widget section.

- [XKCD Comics](#xkcd-comics)
- [Flight Data](#flight-data)
- [NASA APOD](#astronomy-picture-of-the-day)
Expand Down Expand Up @@ -997,6 +998,36 @@ Renders a programming or generic joke. Data is fetched from the [JokesAPI](https

---

### Chuck Norris quotes

Renders a Chuck Norris quote. Data is fetched from the [ChuckNorrisAPI](https://api.chucknorris.io/) by @matchilling. All fields are optional.

<p align="center"><img width="400" src="https://tbd" /></p>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai bot Oct 16, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Add meaningful alt text (and avoid placeholder URLs).

The image uses a placeholder URL and lacks an alt attribute, which breaks accessibility guidelines and ships a dead link. Either provide the real screenshot with descriptive alt text or remove the image block until it’s available.

🧰 Tools
🪛 markdownlint-cli2 (0.18.1)

1005-1005: Images should have alternate text (alt text)

(MD045, no-alt-text)

🤖 Prompt for AI Agents
In docs/widgets.md around line 1005, the image uses a placeholder URL and has no
alt text; replace the placeholder src with the real screenshot URL (or remove
the entire <p> block until the image is available) and add a meaningful alt
attribute describing the image content (e.g., alt="Screenshot of widget settings
panel showing X, Y, Z") to satisfy accessibility and avoid dead links.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In docs/widgets.md around line 1005, the image uses a placeholder URL and has no
alt text; replace the placeholder src with the real screenshot URL (or remove
the entire

block until the image is available) and add a meaningful alt
attribute describing the image content (e.g., alt="Screenshot of widget settings
panel showing X, Y, Z") to satisfy accessibility and avoid dead links.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@khulnasoft-bot, you're right to flag this! The placeholder image should be addressed before merging.

For the Chuck Norris widget documentation, I recommend one of these approaches:

Option 1: Add a real screenshot (preferred)

  1. Take a screenshot of the widget in action showing a Chuck Norris quote
  2. Upload it to i.ibb.co (following the pattern used by other widgets in this file)
  3. Replace the placeholder with the real URL and add descriptive alt text:
<p align="center"><img width="400" src="https://i.ibb.co/[your-image-id]/chuck-norris.png" alt="Chuck Norris widget displaying a quote" /></p>

Option 2: Remove the placeholder (temporary)
If a screenshot isn't ready yet, remove the entire image block for now:

### Chuck Norris quotes

Renders a Chuck Norris quote. Data is fetched from the [ChuckNorrisAPI](https://api.chucknorris.io/) by @matchilling. All fields are optional.

#### Options

This keeps the documentation accessible and avoids dead links until a proper screenshot is available. Looking at other widgets in the file (like the Joke widget just above), they all use properly hosted images with the same hosting service, so following that pattern would maintain consistency.


#### Options

**Field** | **Type** | **Required** | **Description**
--- | --- | --- | ---
**`categories`** | `string` | _Optional_ | Set the category of jokes to return. Use a string to specify a single category, or an array to pass in multiple options. Available options are: `animal`,`career`,`celebrity`,`dev`,`explicit`,`fashion`,`food`,`history`,`money`,`movie`,`music`,`political`,`religion`,`science`,`sport` and `travel`. An up-to-date list of supported categories can be found [here](https://api.chucknorris.io/jokes/categories). Defaults to not explicitely set and therefore any of the categories can come up.

#### Example

```yaml
- type: chucknorris
options:
categories: history,sport
```

#### Info

- **CORS**: 🟢 Enabled
- **Auth**: 🟢 Not Required
- **Price**: 🟢 Free
- **Host**: Managed Instance
- **Privacy**: _See [matchilling's Privacy Policy](https://api.chucknorris.io/privacy)_

---

### XKCD Comics

Have a laugh with the daily comic from [XKCD](https://xkcd.com/). A classic webcomic website covering everything from Linux, math, romance, science and language. All fields are optional.
Expand Down
66 changes: 66 additions & 0 deletions src/components/Widgets/ChuckNorris.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<template>
<div class="chuckNorris-wrapper">
<p class="chuckNorris chuckNorris-line">{{ chuckNorrisLine }}</p>
</div>
</template>

<script>
import axios from 'axios';
import WidgetMixin from '@/mixins/WidgetMixin';
import { widgetApiEndpoints } from '@/utils/defaults';

export default {
mixins: [WidgetMixin],
components: {},
data() {
return {
chuckNorrisLine: null,
};
},
computed: {
/* Format the users preferred categories */
categories() {
let usersChoice = this.options.categories;
if (!usersChoice) return '';
if (Array.isArray(usersChoice)) usersChoice = usersChoice.join(',');
const categories = ["animal","career","celebrity","dev","explicit","fashion","food","history","money","movie","music","political","religion","science","sport","travel"];
if (categories.some((cat) => usersChoice.toLowerCase().includes(cat))) return usersChoice;
return '';
},
Comment thread
FortiShield marked this conversation as resolved.
/* Combine data parameters for the API endpoint */
endpoint() {
if (this.categories !== '') return `${widgetApiEndpoints.chuckNorris}?category=${this.categories}`;
return `${widgetApiEndpoints.chuckNorris}`;
},
},
methods: {
/* Make GET request to ChuckNorris API endpoint */
fetchData() {
axios.get(this.endpoint)
.then((response) => {
this.processData(response.data);
})
.catch((dataFetchError) => {
this.error('Unable to fetch any Chuck Norris quote', dataFetchError);
})
.finally(() => {
this.finishLoading();
});
},
/* Assign data variables to the returned data */
processData(data) {
this.chuckNorrisLine = data?.value || 'No joke available';
},
Comment thread
FortiShield marked this conversation as resolved.
},
};
</script>

<style scoped lang="scss">
.chuckNorris-wrapper {
p.chuckNorris {
color: var(--widget-text-color);
font-size: 1.2rem;
}
}

</style>
7 changes: 3 additions & 4 deletions src/components/Widgets/WidgetBase.vue
Original file line number Diff line number Diff line change
Expand Up @@ -49,14 +49,13 @@ const COMPAT = {
anonaddy: 'addy.io',
apod: 'Apod',
'blacklist-check': 'BlacklistCheck',
chucknorris: 'ChuckNorris',
clock: 'Clock',
'crypto-price-chart': 'CryptoPriceChart',
'crypto-watch-list': 'CryptoWatchList',
'custom-search': 'CustomSearch',
'cve-vulnerabilities': 'CveVulnerabilities',
'domain-monitor': 'DomainMonitor',
'code-stats': 'CodeStats',
'covid-stats': 'CovidStats',
'drone-ci': 'DroneCi',
embed: 'EmbedWidget',
'eth-gas-prices': 'EthGasPrices',
Expand All @@ -80,15 +79,15 @@ const COMPAT = {
'gl-network-traffic': 'GlNetworkTraffic',
'gl-system-load': 'GlSystemLoad',
'gl-cpu-temp': 'GlCpuTemp',
'gluetun-status': 'GluetunStatus',
'health-checks': 'HealthChecks',
'hackernews-trending': 'HackernewsTrending',
'gluetun-status': 'GluetunStatus',
iframe: 'IframeWidget',
image: 'ImageWidget',
joke: 'Jokes',
linkding: 'Linkding',
'mullvad-status': 'MullvadStatus',
mvg: 'Mvg',
linkding: 'Linkding',
'mvg-connection': 'MvgConnection',
'nd-cpu-history': 'NdCpuHistory',
'nd-load-history': 'NdLoadHistory',
Expand Down
1 change: 1 addition & 0 deletions src/utils/defaults.js
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ module.exports = {
anonAddy: 'https://app.addy.io',
astronomyPictureOfTheDay: 'https://apod.as93.net/apod',
blacklistCheck: 'https://api.blacklistchecker.com/check',
chuckNorris: 'https://api.chucknorris.io/jokes/random',
codeStats: 'https://codestats.net/',
covidStats: 'https://disease.sh/v3/covid-19',
cryptoPrices: 'https://api.coingecko.com/api/v3/coins/',
Expand Down
Loading