mirror of
https://github.com/nostr-protocol/nips.git
synced 2024-12-22 08:25:53 -05:00
Set 22.md
This NIP introduces the concept of Smart Widgets for Nostr clients.
This commit is contained in:
parent
fade0164f5
commit
ddd26ebbad
366
22.md
Normal file
366
22.md
Normal file
|
@ -0,0 +1,366 @@
|
|||
This NIP introduces the concept of Smart Widgets for Nostr clients. Smart Widgets are customizable, interactive components that can be embedded within Nostr events. These widgets allow users and developers to create rich, dynamic content that enhances the user experience. This proposal outlines the structure, implementation, and potential use cases of Smart Widgets within the Nostr protocol.
|
||||
|
||||
# 1. Motivation
|
||||
As Nostr continues to grow, the need for more engaging and interactive content increases. Traditional static content limits user interaction and engagement. Smart Widgets provide a way to embed dynamic components, such as buttons, images, and polls, directly into Nostr events, enabling more interactive and responsive content.
|
||||
|
||||
# 2. Specification
|
||||
## 2.1. Event kind
|
||||
A new addressable event kind is proposed for Smart Widgets kind `30031`. This event kind will encapsulate the data necessary to render and interact with the Smart Widgets.
|
||||
## 2.2. Event Structure
|
||||
The `content` of these events should be a stringified JSON of the related smart widget. It is required and can't be empty.
|
||||
|
||||
The list of tags are as follows:
|
||||
- `d` (required) universally unique identifier (UUID). Generated by the client creating the smart widget event.
|
||||
- `title` (optional) title of the smart widget.
|
||||
- `summary` (optional) description of the smart widget and its usage.
|
||||
- `client` (optional) reference for the used client for creating the smart widget.
|
||||
- `published_at` (optional) Publishing time of the smart widget differs from the `created_at` for edited smart widgets.
|
||||
|
||||
```JSON
|
||||
{
|
||||
"id": <32-bytes lowercase hex-encoded SHA-256 of the the serialized event data>,
|
||||
"pubkey": <32-bytes lowercase hex-encoded public key of the event creator>,
|
||||
"created_at": <Unix timestamp in seconds>,
|
||||
"kind": 30031,
|
||||
"content": "<smart_widget_stringified_json>",
|
||||
"tags": [
|
||||
|
||||
["d", "<UUID>"],
|
||||
|
||||
["title", "<title of smart widget event>"],
|
||||
["summary", "<description of smart widget event>"],
|
||||
|
||||
["client", "<client_name>", "31990:app1_pubkey:<d_identifier>"],
|
||||
["published_at", "<Unix timestamp in seconds>"],
|
||||
|
||||
]
|
||||
}
|
||||
```
|
||||
## 2.3. Positional Key Elements:
|
||||
The `content` property should contain a stringified object that defines the structure of the smart widget, it contains three main properties:
|
||||
```JSON
|
||||
{
|
||||
"background_color": "<string_hex_color_code>",
|
||||
"border_color": "<string_hex_color_code> | <empty_string>", // leave empty if not specified
|
||||
"components": "<array_of_objects>"
|
||||
}
|
||||
```
|
||||
### 2.3.1. background_color
|
||||
the `background_color` property defines the color of the overall container of the smart widget.
|
||||
### 2.3.3. border_color
|
||||
the `border_color` property defines the border color of the overall container of the smart widget, it should be left as an empty string if the user wants to use the client default design
|
||||
### 2.3.3. components
|
||||
The `components` property defines a list of grids which together forms a smart widget. Each grid is defined by a `layout`, `division`, `left_side`, and/or `right_side` and represents either a single component for `mono` layouts or a set of components for `duo` layouts within the smart widget. Each component can hold a single value from these pre-defined types: `text`, `button`,`image`, `video`, `zap-poll`
|
||||
### 2.3.3.1. layout
|
||||
The `layout` property defines the arrangement of components within the smart widget. It can be set to `mono` layout (type 1), which includes a default `left_side`, or to `duo` layout (type 2), which features both `left_side` and `right_side`. In the `duo` layout, components can be stacked vertically on each side, forming a compact grid, where the division parameter determines the alignment and balance between the grid’s sides.
|
||||
```json
|
||||
// mono layout selection
|
||||
"layout": 1
|
||||
// duo layout selection
|
||||
"layout": 2
|
||||
```
|
||||
### 2.3.3.2. division
|
||||
The `division` property defines the spatial ratio between the `left` and `right` sides in a `duo layout`. It is typically expressed as ratios like `1:1` (also applicable to `mono layout` by default), `1:2`, or `2:1`. This enables flexible layout designs, allowing one side to occupy more or less space to achieve the desired visual balance.
|
||||
```json
|
||||
// 1:1 division ratio selection for mono (by default) and duo layouts
|
||||
"division": "1:1"
|
||||
|
||||
// 1:2 division ratio selection for duo layouts
|
||||
// left side 1 third, right side 2 thirds
|
||||
"division": "1:2"
|
||||
|
||||
// 2:1 division ratio selection for duo layouts
|
||||
// left side 2 thirds, right side 1 third
|
||||
"division": "2:1"
|
||||
```
|
||||
### 2.3.3.3. left_side and right_side
|
||||
The `left_side` and `right_side` properties specify the components that appear on each side of the grid. Each side can contain one or more components, such as text, images, or buttons. These components can be dynamically adjusted based on the division and layout settings.
|
||||
|
||||
In this example, the left side contains a header 2 text, while the right side is left empty, hence it could be either a `mono layout` or a `duo layout` with a null `right side`.
|
||||
```json
|
||||
"left_side": [
|
||||
{
|
||||
"type": "text",
|
||||
"metadata": {
|
||||
"size": "h2",
|
||||
"content": "Header 2 Title",
|
||||
"text_color": "#e4d107"
|
||||
}
|
||||
}
|
||||
],
|
||||
"right_side": [] // or null
|
||||
```
|
||||
|
||||
Based on the description above, the structure of the `content` property should be as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
//smart widget properties
|
||||
"background_color": "<hex_color_code>",
|
||||
"border_color": "<hex_color_code> | <empty_string>", // leave empty if not specified
|
||||
|
||||
//used components
|
||||
"components": [
|
||||
// grid_1
|
||||
{
|
||||
// 1 for mono layout and 2 for duo layout
|
||||
"layout": "<layout_type>",
|
||||
|
||||
// "1:1" for mono layout (default) | "1:1", "1:2", or "2:1" for duo layout
|
||||
"division": "<ratio>",
|
||||
|
||||
// default for mono layout | optional for duo layout
|
||||
"left_side": [
|
||||
{
|
||||
|
||||
// the component can be: text | button | image | video | zap-poll
|
||||
"type": "<component_type>",
|
||||
"metadata": {
|
||||
// each component type has its own pre-defined set of keys, these keys are discussed below
|
||||
"key": "<value>",
|
||||
...
|
||||
}
|
||||
}
|
||||
],
|
||||
// null for mono layout | optional for duo layout
|
||||
"right_side": [
|
||||
{
|
||||
"type": "<component_type>",
|
||||
"metadata": {
|
||||
"key": "<value>",
|
||||
...
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
// grid_2,3,n
|
||||
...
|
||||
]
|
||||
}
|
||||
```
|
||||
## 2.4. Types and Metadata
|
||||
Components are the building blocks of Smart Widgets. Each component is defined by its `type` and associated `metadata`. The components are arranged within each grid of the smart widget using a flexible layout system, divided into left and right sides if wanted as discussed above.
|
||||
### 2.4.1 Text Component
|
||||
Text component is the type `text` with the following metadata:
|
||||
- `content` Text to display.
|
||||
- `text_color` Color of the text.
|
||||
- `weight` Text weight (`regular`, `bold`).
|
||||
- `size` Size of the text (`h1`, `h2`, `regular`, `small`).
|
||||
```json
|
||||
"left_side":
|
||||
[
|
||||
{
|
||||
"type": "text",
|
||||
"metadata": {
|
||||
"content": "<content_to_display>",
|
||||
"text_color": "<hex_color_code>",
|
||||
"weight": "regular | bold",
|
||||
"size": "h1 | h2 | regular | body"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
### 2.4.2. Button Component
|
||||
Button component is the type `button` with the following metadata:
|
||||
- `content` Label of the button.
|
||||
- `text_color` Color of the button text.
|
||||
- `url` URL to be opened upon clicking the button or invoice/lightning address for zap-related actions.
|
||||
- `pubkey` (Optional) Public key of zap-related actions.
|
||||
- `background_color` Background color of the button.
|
||||
- `type` Type of the button, which can be:
|
||||
- `regular` A standard button that links to a URL.
|
||||
- `nostr` A button that handles Nostr URIs.
|
||||
- `zap` A button that handles Lightning zaps actions.
|
||||
**Regular buttons designated exclusively for social platforms only:**
|
||||
- `youtube` A semi-regular button for linking Youtube-related links.
|
||||
- `telegram` A semi-regular button for linking telegram-related links.
|
||||
- `discord` A semi-regular button for linking discord-related links.
|
||||
- `x` A semi-regular button for linking x-related links.
|
||||
```json
|
||||
[
|
||||
{
|
||||
"type": "button",
|
||||
"metadata": {
|
||||
"content": "<label_of_button>",
|
||||
"text_color": "<hex_color_code> | <empty_string>", // leave empty string to use the default client design
|
||||
"url": "<url> | <n*_scheme> | <lightning_address> | <lnbc>",
|
||||
"pubkey": "<32-bytes lowercase hex-encoded public key of the person to zap>", // optional for zap-related actions such as searching users.
|
||||
"background_color": "<hex_color_code> | <empty_string>", // leave empty string to use the default client design
|
||||
"type": "regular | nostr | zap | youtube | telegram | discord | x"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
### 2.4.3. Image Component
|
||||
Image component is the type `image` with the following metadata:
|
||||
- `url` URL of the image.
|
||||
- `aspect_ratio` Aspect ratio of the image (`16:9`, `1:1`).
|
||||
```json
|
||||
"left_side":
|
||||
[
|
||||
{
|
||||
"type": "image",
|
||||
"metadata": {
|
||||
"url": "<img_url>",
|
||||
"aspect_ratio": "16:9 | 1:1"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
### 2.4.4 Video Component
|
||||
Video component is the type `video` with the following metadata:
|
||||
- `url` URL of the image.
|
||||
```json
|
||||
"left_side":
|
||||
[
|
||||
{
|
||||
"type": "video",
|
||||
"metadata": {
|
||||
"url": "<video_url>",
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
### 2.4.5 Zap Poll Component
|
||||
Zap poll component is the type `zap-poll` with the following metadata:
|
||||
- `content` stringified zap poll event `kind-6969`.
|
||||
- `content_text_color` Color of the poll content text.
|
||||
- `options_text_color` Color of the poll options text.
|
||||
- `options_background_color` Color of the options background.
|
||||
- `options_foreground_color` Color of the options foreground (stats progress).
|
||||
```json
|
||||
"left_side":
|
||||
[
|
||||
{
|
||||
"type": "zap-poll",
|
||||
"metadata": {
|
||||
"content": "<stringified_zap_poll_event>", //avoiding using n*
|
||||
"content_text_color": "<hex_color_code>",
|
||||
"options_text_color": "<hex_color_code>",
|
||||
"options_background_color": "<hex_color_code>",
|
||||
"options_foreground_color": "<hex_color_code>"
|
||||
}
|
||||
}
|
||||
]
|
||||
```
|
||||
|
||||
### 2.4.6. Illustrative Example
|
||||
In this example, `mono` and `duo` layouts are used, where `layout:1` represents `mono layout` with a null right side and `layout:2` represents `duo layout` with added components in both left and right sides. Only the component type `text` has been used for demonstration purposes.
|
||||
```json
|
||||
{// mono layout
|
||||
"border_color": "#343434",
|
||||
"background_color": "#252429",
|
||||
"components": [
|
||||
{
|
||||
"layout": 1, // mono layout reference
|
||||
"division": "1:1", // disivion is 1:1 by default for mono layouts
|
||||
"left_side": [
|
||||
{
|
||||
// component of type "text"
|
||||
"type": "text",
|
||||
"metadata": {
|
||||
//specified keys for a component of a type "text"
|
||||
"content": "This is a bold text in a mono layout",
|
||||
"text_color": "#85318b",
|
||||
"weight": "bold",
|
||||
"size": "regular"
|
||||
}
|
||||
}
|
||||
],
|
||||
"right_side": [] // or null
|
||||
},
|
||||
{// duo layout
|
||||
"layout": 2, // duo layout reference
|
||||
"division": "1:1", // "1:1","2:1", or "1:2"
|
||||
"left_side": [
|
||||
{
|
||||
"type": "text",
|
||||
"metadata": {
|
||||
"content": "This is a bold text in a duo layout left side",
|
||||
"text_color": "#fbe6bc",
|
||||
"weight": "bold",
|
||||
"size": "regular"
|
||||
}
|
||||
}
|
||||
],
|
||||
"right_side": [
|
||||
{
|
||||
"type": "text",
|
||||
"metadata": {
|
||||
"content": "This is a regular text in a duo layout right side",
|
||||
"text_color": "#fe9b0a",
|
||||
"weight": "regular",
|
||||
"size": "regular"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## 3. Smart Widget Example
|
||||
This conclusive example illustrates the various components that can be utilized within the smart widget, demonstrating how they visually appear with layout and division settings. It also provides a clear picture of how the final Nostr event will appear once the building process is complete.
|
||||
|
||||
A stringified JSON of the kind `30031` smart widget would look like this:
|
||||
```json
|
||||
{
|
||||
"content": "{\"border_color\":\"#b3b3b3\",\"background_color\":\"#1a1a1a\",\"components\":[{\"layout\":1,\"division\":\"1:1\",\"left_side\":[{\"type\":\"text\",\"metadata\":{\"content\":\"Demonstrating Nostr Components types \",\"text_color\":\"#ffffff\",\"weight\":\"\",\"size\":\"h1\"}}],\"right_side\":null},{\"layout\":2,\"division\":\"1:1\",\"left_side\":[{\"type\":\"button\",\"metadata\":{\"content\":\"Regular\",\"text_color\":\"#000000\",\"url\":\"https://nostr.com\",\"pubkey\":\"\",\"background_color\":\"#99ccff\",\"type\":\"regular\"}},{\"type\":\"button\",\"metadata\":{\"content\":\"Nostr URI\",\"text_color\":\"\",\"url\":\"npub14ya7n7czu3kyq3m2sn6kjawmtq6ll6kerrlm90gz9yzfjmflmsxq8dsn39\",\"pubkey\":\"\",\"background_color\":\"\",\"type\":\"nostr\"}}],\"right_side\":[{\"type\":\"button\",\"metadata\":{\"content\":\"Zap Button \",\"text_color\":\"#fbe6bc\",\"url\":\"sungchest@getalby.com\",\"pubkey\":\"a93be9fb02e46c40476a84f56975db5835ffead918ffb2bd022904996d3fdc0c\",\"background_color\":\"#fe9b0a\",\"type\":\"zap\"}},{\"type\":\"button\",\"metadata\":{\"content\":\"External Social Platform\",\"text_color\":\"\",\"url\":\"https://youtube.com\",\"pubkey\":\"\",\"background_color\":\"\",\"type\":\"youtube\"}}]},{\"layout\":1,\"division\":\"1:1\",\"left_side\":[{\"type\":\"image\",\"metadata\":{\"url\":\"https://miro.medium.com/v2/resize:fit:1400/1*IqL9MfRP7Yu90eoxHvdX9Q.png\",\"aspect_ratio\":\"1:1\"}}],\"right_side\":null},{\"layout\":1,\"division\":\"1:1\",\"left_side\":[{\"type\":\"video\",\"metadata\":{\"url\":\"https://www.youtube.com/watch?v=FRKnmj6tTWY\"}}],\"right_side\":null},{\"layout\":1,\"division\":\"1:1\",\"left_side\":[{\"type\":\"zap-poll\",\"metadata\":{\"content\":\"{\\\"content\\\":\\\"What’s your favourite verse from the song NO ALGO? #noalgo\\\",\\\"created_at\\\":1723814685,\\\"id\\\":\\\"3592f4b5bd241047586b3f00388aeb62eae0b5d48b115a19fd44c55c08ba1af6\\\",\\\"kind\\\":6969,\\\"pubkey\\\":\\\"4ce6abbd68dab6e9fdf6e8e9912a8e12f9b539e078c634c55a9bff2994a514dd\\\",\\\"sig\\\":\\\"cdb2695a5d2567a29d24f0a7c27b9f4d83573d5389b6747d8d99c7a253f82db033e90c1cf9be3b4c2a49df6e213200213e2896bceca1018e571728eebb98608e\\\",\\\"tags\\\":[[\\\"t\\\",\\\"noalgo\\\"],[\\\"p\\\",\\\"4ce6abbd68dab6e9fdf6e8e9912a8e12f9b539e078c634c55a9bff2994a514dd\\\",\\\"wss://nostr-01.yakihonne.com\\\"],[\\\"poll_option\\\",\\\"0\\\",\\\"Kweks v1\\\"],[\\\"poll_option\\\",\\\"1\\\",\\\"René v2\\\"],[\\\"poll_option\\\",\\\"2\\\",\\\"Malik v3\\\"],[\\\"closed_at\\\",\\\"1723842000\\\"],[\\\"value_minimum\\\",\\\"3\\\"],[\\\"value_maximum\\\",\\\"21\\\"]]}\",\"content_text_color\":\"#ffffff\",\"options_text_color\":\"#ffffff\",\"options_background_color\":\"#343434\",\"options_foreground_color\":\"#ff7b00\"}}],\"right_side\":null}]}",
|
||||
"created_at": 1724727555,
|
||||
"id": "ac48ae855be4217e34a97f4f3abd4b9237e62ee049374136bd4db632df67e4e6",
|
||||
"kind": 30031,
|
||||
"pubkey": "a93be9fb02e46c40476a84f56975db5835ffead918ffb2bd022904996d3fdc0c",
|
||||
"sig": "907b5153885e302def94cc1d2d1fbaab693e2006546046c9f9ac3c7158c754890c9cc1b19f442a6c5a6915cf3f6551ff112f904294322ce3c1d22293a3987867",
|
||||
"tags": [
|
||||
[
|
||||
"d",
|
||||
"ZblHUjnKR0kFuJSvx24Rn"
|
||||
],
|
||||
[
|
||||
"client",
|
||||
"Yakihonne",
|
||||
"31990:20986fb83e775d96d188ca5c9df10ce6d613e0eb7e5768a0f0b12b37cdac21b3:1700732875747"
|
||||
],
|
||||
[
|
||||
"published_at",
|
||||
"1724727555"
|
||||
],
|
||||
[
|
||||
"title",
|
||||
"Demonstrating Smart Widget components types"
|
||||
],
|
||||
[
|
||||
"summary",
|
||||
"Illustrating purposes"
|
||||
]
|
||||
]
|
||||
}
|
||||
```
|
||||
A visual represenation of the nostr event above will be like this:
|
||||
|
||||
|
||||
![visual-illustration](https://image.nostr.build/4c1f08cb84bf4ea721404cf00ae0f456b320d05ac30488828079560ed3d3d7e5.png)
|
||||
|
||||
Accessible via the following link: [Demonstrating Smart Widget components types](https://yakihonne.com/naddr1qvzqqqr4fupzp2fma8as9ervgprk4p84d96akkp4ll4djx8lk27sy2gyn9knlhqvqq245cnvfp2k5mjt2gcxk3n4fffhv7pjx3fxuwewmcf)
|
||||
|
||||
|
||||
## 4. Implementation
|
||||
This NIP can be implemented by Nostr clients willing to adopt the `kind-30031` events. The implementation involves parsing the `content` property, rendering the smart widgets according to the defined components, and ensuring interactivity where applicable.
|
||||
### 4.1. Building
|
||||
Smart widgets builders provide a comprehensive toolset for people looking to create customized widgets tailored to their specific needs on Nostr for their audience. These builders allow people to design smart widgets by combine a set of various components such as text, images, butons, and more, within flexible layouts and divisions. The user-friendly interface simplifies the process, enabling dynamic adjustments and real-time previews. While some clients offer robust smart widgets builders, all clients are free to create their own, as long as they ensure compability and interporability with the validation standards used by all clients.
|
||||
Example: [YakiHonne smart widgets builder](https://yakihonne.com/smart-widget-builder)
|
||||
![building-illustration](https://image.nostr.build/13496134d5f525928933fe21674381eeb6eb2859161df5abf280ffb5f48e480e.png)
|
||||
### 4.2. Usage
|
||||
Once a smart widget has been successfully built and validated, it is ready to be rendered and parsed by other Nostr clients. These clients interpret the widget's structure and display its components according to the specifications set during the building process. The smart widget is parsed to ensure that its layout, division, and individual components– such as text, images, and buttons– are displayed as intended. This seamless integration allows for a consistent and interactive user experience across various clients, ensuring that the smart widget functions correctly and looks the same regardless of the client being used.
|
||||
|
||||
Smart widgets offer the flexibility to be embedded directly into both short notes `kind-1` and long-form content `kind-30023`, similar to how any Nostr event URI scheme can be embedded. This capability allows people to seamlessly integrate interactive elements, like buttons, images, and polls, within their posts or articles via smart widgets, enhancing the overall content experience. By simply referencing the smart widget Nostr event URI scheme `naddr`, people can enrich their content with dynamic, intreactive components that are rendered consistenly across different clients, making the content more engaging anf functional.
|
||||
Example: [Embedding Smart Widgets into short notes](https://yakihonne.com/notes)
|
||||
![embedding-illustration](https://image.nostr.build/ddcafdd5af4a6b0f0ce968f694d9c628c37b5eb1a4bd4ccfcc3ed161f864038b.png)
|
||||
### 4.3. Validation
|
||||
After building a smart widget, it's crucial to verify its integrity and functionality using a smart widgets checker. This tool validates the widget's sturcture, ensuring that all components are correctly configured and comply with the smart widgets nip. It also allows identifiying any unknown components or keys for other clients to add its support. The validation process is vital for maintaining consistency and reliability across different clients. While various clients offer such checkers, all clients are also encouraged to build their own, as long as the validation process is consistent with standards recognized by all Nostr clients, ensuring a seamless user experience across the network.
|
||||
Example: [YakiHonne smart widgets checker](https://yakihonne.com/smart-widget-checker)
|
||||
![checker-illustration](https://image.nostr.build/d3947730f2c65212143ab61457e2e7eb25ff32b5fd588ab0f3c1893f64c2fd3b.png)
|
||||
## 5. Security Concerns
|
||||
To ensure security and prevent misuse, clients should implement safeguards to prevent malicious content or links within smart widgets.
|
||||
## 6. Conclusion
|
||||
Smart Widgets offer a powerful way to enhance user interaction and content presentation within the Nostr protocol. By adopting this NIP, clients can provide richer experiences, drive user engagement, and support various innovative use cases.
|
Loading…
Reference in New Issue
Block a user