7 changed files with 407 additions and 0 deletions
@ -0,0 +1,64 @@
@@ -0,0 +1,64 @@
|
||||
import { Canvas, Meta, Story } from '@storybook/addon-docs'; |
||||
|
||||
<Meta title="owncast/Documentation/Design" /> |
||||
|
||||
# Owncast Design Guidelines & Resources |
||||
|
||||
A collection of design contribution guidelines and resources for the Owncast interface. |
||||
|
||||
> **All participating designers are highly encouraged to shape and evolve these guidelines!** |
||||
> It is a work in progress and as we have design contributors we can work to solidify the process, tools and resources. |
||||
|
||||
## 👋 Welcome |
||||
|
||||
Owncast is a is a live streaming and chat server targeted to anybody who has live streaming needs. This means anybody from corporate events, government meetings, game streamers, musicians, churches, TV stations, and more. |
||||
|
||||
Read the detailed [product definition](https://github.com/owncast/owncast/blob/develop/docs/product-definition.md) to learn more. |
||||
|
||||
## 🚢 How to contribute to product design |
||||
|
||||
1. Check out open [issues](https://github.com/owncast/owncast/issues) here on GitHub (we label them with `needs design`) |
||||
2. Feel free to open an issue on your own if you find something you would like to contribute to the project. |
||||
3. Add your contributions to an issue and we promise we will review your contribution carefully and foster discussions |
||||
|
||||
**We encourage you to:** |
||||
|
||||
- Get in touch with the team by joining our [Community Chat](https://owncast.rocket.chat). |
||||
- Check out our [Contributor Guide](https://owncast.online/help) and |
||||
[Code of Conduct](https://github.com/owncast/owncast/blob/develop/CODE_OF_CONDUCT.md) |
||||
|
||||
## 🎭 Target audience |
||||
|
||||
Owncast is a is a live streaming and chat server targeted to anybody who has live streaming needs. This means anything from corporate events, government meetings, game streams, concerts, TV stations, and more. |
||||
|
||||
## 💅 Design relevant materials |
||||
|
||||
Here is a list of design relevant information and materials: |
||||
|
||||
### Fonts |
||||
|
||||
http://owncast.online/components/?path=/story/owncast-style-guide-typography--page |
||||
|
||||
Body text: OpenSans |
||||
|
||||
Display/Header text: Poppins |
||||
|
||||
### Colors |
||||
|
||||
http://owncast.online/components/?path=/story/owncast-style-guide-default-theme--page |
||||
|
||||
### Design Files, Screenshots, etc |
||||
|
||||
We do not currently have any design files that fully represent the state of |
||||
the Owncast interface. However going forward it would be nice to resolve this |
||||
and collaborate on designs. |
||||
|
||||
We do have a [PenPot organization](https://design.penpot.app/#/dashboard/team/8373f780-f255-11ec-b774-f940e3befd53/projects). Please ask for access. |
||||
|
||||
## 🎓 License |
||||
|
||||
All design work is licensed under the |
||||
[MIT](https://mit-license.org/) |
||||
|
||||
[(Back to top)](#-table-of-contents) |
||||
|
||||
@ -0,0 +1,130 @@
@@ -0,0 +1,130 @@
|
||||
|
||||
|
||||
import { Canvas, Meta, Story } from '@storybook/addon-docs'; |
||||
import { Image, ImageRow } from './ImageAsset'; |
||||
|
||||
<Meta title="owncast/Assets/Emoji" /> |
||||
|
||||
# Built-in Custom Emoji |
||||
|
||||
|
||||
## Blob |
||||
<a href="/img/emoji/blob/LICENSE.md" target="_blank"> |
||||
LICENSE |
||||
</a> |
||||
<ImageRow images={[ |
||||
{src: "/img/emoji/blob/ablobattention.gif", name: "ablobattention.gif"}, |
||||
{src: "/img/emoji/blob/ablobaww.gif", name: "ablobaww.gif"}, |
||||
{src: "/img/emoji/blob/ablobblewobble.gif", name: "ablobblewobble.gif"}, |
||||
{src: "/img/emoji/blob/ablobcheer.gif", name: "ablobcheer.gif"}, |
||||
{src: "/img/emoji/blob/ablobcry.gif", name: "ablobcry.gif"}, |
||||
{src: "/img/emoji/blob/ablobdancer.gif", name: "ablobdancer.gif"}, |
||||
{src: "/img/emoji/blob/ablobgift.gif", name: "ablobgift.gif"}, |
||||
{src: "/img/emoji/blob/ablobgiggle.gif", name: "ablobgiggle.gif"}, |
||||
{src: "/img/emoji/blob/ablobparty.gif", name: "ablobparty.gif"}, |
||||
{src: "/img/emoji/blob/ablobsleep.gif", name: "ablobsleep.gif"}, |
||||
{src: "/img/emoji/blob/ablobthinking.gif", name: "ablobthinking.gif"}, |
||||
{src: "/img/emoji/blob/ablobwave.gif", name: "ablobwave.gif"}, |
||||
{src: "/img/emoji/blob/blobangry.png", name: "blobangry.png"}, |
||||
{src: "/img/emoji/blob/blobaww.png", name: "blobaww.png"}, |
||||
{src: "/img/emoji/blob/blobdancer.png", name: "blobdancer.png"}, |
||||
{src: "/img/emoji/blob/blobjam.png", name: "blobjam.png"}, |
||||
{src: "/img/emoji/blob/blobscream.png", name: "blobscream.png"}, |
||||
{src: "/img/emoji/blob/blobthanks.png", name: "blobthanks.png"}, |
||||
{src: "/img/emoji/blob/blobthinking.png", name: "blobthinking.png"}, |
||||
{src: "/img/emoji/blob/blobwave.png", name: "blobwave.png"}, |
||||
{src: "/img/emoji/blob/blobyes.png", name: "blobyes.png"}, |
||||
{src: "/img/emoji/blob/blobyum.png", name: "blobyum.png"}, |
||||
]}/> |
||||
|
||||
|
||||
## Conigliolo96 |
||||
<a href="/img/emoji/conigliolo96/LICENSE.md" target="_blank"> |
||||
LICENSE |
||||
</a> |
||||
<ImageRow images={[ |
||||
{src: "/img/emoji/conigliolo96/conigliolo1.gif", name: "conigliolo1.gif"}, |
||||
{src: "/img/emoji/conigliolo96/conigliolo15.gif", name: "conigliolo15.gif"}, |
||||
{src: "/img/emoji/conigliolo96/conigliolo17.gif", name: "conigliolo17.gif"}, |
||||
{src: "/img/emoji/conigliolo96/conigliolo21.gif", name: "conigliolo21.gif"}, |
||||
{src: "/img/emoji/conigliolo96/conigliolo25.gif", name: "conigliolo25.gif"}, |
||||
{src: "/img/emoji/conigliolo96/conigliolo28.gif", name: "conigliolo28.gif"}, |
||||
]}/> |
||||
|
||||
|
||||
## Dog |
||||
<a href="/img/emoji/dog/LICENSE.md" target="_blank"> |
||||
LICENSE |
||||
</a> |
||||
<ImageRow images={[ |
||||
{src: "/img/emoji/dog/img001.svg", name: "img001.svg"}, |
||||
{src: "/img/emoji/dog/img091.svg", name: "img091.svg"}, |
||||
{src: "/img/emoji/dog/img093.svg", name: "img093.svg"}, |
||||
{src: "/img/emoji/dog/img203.svg", name: "img203.svg"}, |
||||
{src: "/img/emoji/dog/img288.svg", name: "img288.svg"}, |
||||
{src: "/img/emoji/dog/img327.svg", name: "img327.svg"}, |
||||
{src: "/img/emoji/dog/img346.svg", name: "img346.svg"}, |
||||
{src: "/img/emoji/dog/img347.svg", name: "img347.svg"}, |
||||
{src: "/img/emoji/dog/img352.svg", name: "img352.svg"}, |
||||
]}/> |
||||
|
||||
|
||||
## Mutant |
||||
<a href="/img/emoji/mutant/LICENSE.md" target="_blank"> |
||||
LICENSE |
||||
</a> |
||||
<ImageRow images={[ |
||||
{src: "/img/emoji/mutant/8_ball.svg", name: "8_ball.svg"}, |
||||
{src: "/img/emoji/mutant/alien.svg", name: "alien.svg"}, |
||||
{src: "/img/emoji/mutant/american_football.svg", name: "american_football.svg"}, |
||||
{src: "/img/emoji/mutant/arms_in_the_air.svg", name: "arms_in_the_air.svg"}, |
||||
{src: "/img/emoji/mutant/artist.svg", name: "artist.svg"}, |
||||
{src: "/img/emoji/mutant/astronaut.svg", name: "astronaut.svg"}, |
||||
{src: "/img/emoji/mutant/back_of_hand_clw.svg", name: "back_of_hand_clw.svg"}, |
||||
{src: "/img/emoji/mutant/back_of_hand_hoof.svg", name: "back_of_hand_hoof.svg"}, |
||||
{src: "/img/emoji/mutant/back_of_hand_paw.svg", name: "back_of_hand_paw.svg"}, |
||||
{src: "/img/emoji/mutant/baseball.svg", name: "baseball.svg"}, |
||||
{src: "/img/emoji/mutant/basketball.svg", name: "basketball.svg"}, |
||||
{src: "/img/emoji/mutant/blep.svg", name: "blep.svg"}, |
||||
{src: "/img/emoji/mutant/bow_b3.svg", name: "bow_b3.svg"}, |
||||
{src: "/img/emoji/mutant/cat_crying.svg", name: "cat_crying.svg"}, |
||||
{src: "/img/emoji/mutant/cat_devious.svg", name: "cat_devious.svg"}, |
||||
{src: "/img/emoji/mutant/cat_grin.svg", name: "cat_grin.svg"}, |
||||
{src: "/img/emoji/mutant/cat_heart_eyes.svg", name: "cat_heart_eyes.svg"}, |
||||
{src: "/img/emoji/mutant/cat_joy.svg", name: "cat_joy.svg"}, |
||||
{src: "/img/emoji/mutant/cat_kiss.svg", name: "cat_kiss.svg"}, |
||||
{src: "/img/emoji/mutant/cat_pouting.svg", name: "cat_pouting.svg"}, |
||||
{src: "/img/emoji/mutant/cat_scream.svg", name: "cat_scream.svg"}, |
||||
{src: "/img/emoji/mutant/cat_smile.svg", name: "cat_smile.svg"}, |
||||
{src: "/img/emoji/mutant/chef.svg", name: "chef.svg"}, |
||||
{src: "/img/emoji/mutant/detective.svg", name: "detective.svg"}, |
||||
{src: "/img/emoji/mutant/ear.svg", name: "ear.svg"}, |
||||
{src: "/img/emoji/mutant/eye.svg", name: "eye.svg"}, |
||||
{src: "/img/emoji/mutant/eyes.svg", name: "eyes.svg"}, |
||||
{src: "/img/emoji/mutant/facepalm.svg", name: "facepalm.svg"}, |
||||
{src: "/img/emoji/mutant/football.svg", name: "football.svg"}, |
||||
{src: "/img/emoji/mutant/ghost.svg", name: "ghost.svg"}, |
||||
{src: "/img/emoji/mutant/grumpy_block.svg", name: "grumpy_block.svg"}, |
||||
{src: "/img/emoji/mutant/hot_shit.svg", name: "hot_shit.svg"}, |
||||
{src: "/img/emoji/mutant/jack_o_lantern.svg", name: "jack_o_lantern.svg"}, |
||||
{src: "/img/emoji/mutant/long_pointed_ear.svg", name: "long_pointed_ear.svg"}, |
||||
{src: "/img/emoji/mutant/mechanical_arm.svg", name: "mechanical_arm.svg"}, |
||||
{src: "/img/emoji/mutant/no_good.svg", name: "no_good.svg"}, |
||||
{src: "/img/emoji/mutant/office_worker.svg", name: "office_worker.svg"}, |
||||
{src: "/img/emoji/mutant/ok_gesture.svg", name: "ok_gesture.svg"}, |
||||
{src: "/img/emoji/mutant/person_frowning.svg", name: "person_frowning.svg"}, |
||||
{src: "/img/emoji/mutant/raising_hand.svg", name: "raising_hand.svg"}, |
||||
{src: "/img/emoji/mutant/robot.svg", name: "robot.svg"}, |
||||
{src: "/img/emoji/mutant/shrug.svg", name: "shrug.svg"}, |
||||
{src: "/img/emoji/mutant/singer.svg", name: "singer.svg"}, |
||||
{src: "/img/emoji/mutant/skull.svg", name: "skull.svg"}, |
||||
{src: "/img/emoji/mutant/skull_and_crossbones.svg", name: "skull_and_crossbones.svg"}, |
||||
{src: "/img/emoji/mutant/softball.svg", name: "softball.svg"}, |
||||
{src: "/img/emoji/mutant/student.svg", name: "student.svg"}, |
||||
{src: "/img/emoji/mutant/studio_microphone.svg", name: "studio_microphone.svg"}, |
||||
{src: "/img/emoji/mutant/technologist.svg", name: "technologist.svg"}, |
||||
{src: "/img/emoji/mutant/tennis.svg", name: "tennis.svg"}, |
||||
{src: "/img/emoji/mutant/volleyball.svg", name: "volleyball.svg"}, |
||||
]}/> |
||||
|
||||
|
||||
@ -0,0 +1,91 @@
@@ -0,0 +1,91 @@
|
||||
import { Canvas, Meta, Story } from '@storybook/addon-docs'; |
||||
|
||||
<Meta title="owncast/Documentation/Product Definition" /> |
||||
|
||||
# Owncast Product Definition |
||||
|
||||
## Why |
||||
|
||||
By defining the goals and target user bases we have something stable to guide decisions, features, conversations and keep clarity around what is being built. |
||||
|
||||
While these definitions and lists should not be seen as exhaustive, in theory, once this is seen as "complete" there should be few, if any changes, as that would note a large change in direction and goals. |
||||
|
||||
[TOC] |
||||
|
||||
## Vision |
||||
|
||||
> The out-of-the-box personal broadcast platform for DIY streamers and integrators. |
||||
|
||||
## Primary Goals |
||||
|
||||
- Useful out of the box. |
||||
- Fast to get running. |
||||
- Self-contained. |
||||
- An alternative, not a competitor. |
||||
- For individuals, not service providers. |
||||
- Easy to integrate into other projects/products. |
||||
- Low barrier to entry. |
||||
- Empowering. |
||||
- Customizable and hackable. |
||||
|
||||
## Primary Users |
||||
|
||||
### The DIY Streamer |
||||
|
||||
An individual who is streaming as a hobby, a project, or is moving their audience from an existing streaming platform. |
||||
|
||||
**Needs**: |
||||
|
||||
- Security/ownership of their own stream. |
||||
- Building an independent space. |
||||
- Personalization. |
||||
- Tools to manage a relationship with their audience. |
||||
|
||||
**Pains**: |
||||
|
||||
- Kicked off other streaming services. |
||||
- Feeling of inequality or bias. |
||||
- Their content has low visibility. |
||||
- Platform rules do not align with them. |
||||
- Do not agree with the forced ads, tracking and analytics. |
||||
|
||||
### The Integrator |
||||
|
||||
An individual or organization that has existing content, products or platforms that they want to add live streaming functionality to. |
||||
|
||||
**Needs**: |
||||
|
||||
- Broadcasting without censorship. |
||||
- Full ownership of their brand. |
||||
- Embedding and 3rd party playback. |
||||
- Support private or invite-only streams. |
||||
- Independence. |
||||
|
||||
**Pains**: |
||||
|
||||
- Censorship. |
||||
- Rules. |
||||
- Ads. |
||||
- Risk of losing viewers from competitors and distractions. |
||||
|
||||
**Desires**: |
||||
|
||||
- Hosting events. |
||||
- Running their own broadcasting service. |
||||
|
||||
## Secondary Users |
||||
|
||||
### The Viewer |
||||
|
||||
An audience member that is often, but not always, taking part in chat. |
||||
|
||||
**Needs**: |
||||
|
||||
- To watch high quality video. |
||||
- Ways to interact with the streamer. Chat, memes, emoji. |
||||
- Calls to actions, links, next steps. |
||||
|
||||
**Pains**: |
||||
|
||||
- Understanding the interface and knowing they're in the correct place. |
||||
|
||||
@ -0,0 +1,78 @@
@@ -0,0 +1,78 @@
|
||||
import { Canvas, Meta, Story } from '@storybook/addon-docs'; |
||||
|
||||
<Meta title="owncast/Documentation/Building Web Components" /> |
||||
|
||||
# How we develop components |
||||
|
||||
This document outlines how we develop the components for the Owncast Web UI. |
||||
|
||||
You should use this document as a guide when making changes to existing components, and adding new ones. |
||||
Working with the same development process help keep the project maintainable. |
||||
|
||||
## What are components |
||||
|
||||
A component in React is a custom HTML element. They're included in the DOM just like regular elements `<ChatBox /`>. |
||||
|
||||
## Functional Components |
||||
|
||||
In react, there's two ways to write a component: there's Class-based Components, and Functional Components. |
||||
|
||||
Class-based is older and has fallen out of favor. |
||||
Functional Components are the new standard and you'll find them in most React projects written today. |
||||
|
||||
See the [React Functional Component docs](https://reactjs.org/docs/components-and-props.html) for more info. |
||||
|
||||
### How we write Functional Components |
||||
|
||||
We've defined a pattern for how we write Functional Components in the Owncast Web UI. |
||||
There's a few ways to to write Functional Components that are common, so defining a standard helps keep this project readable and consistent. |
||||
|
||||
The pattern we've settled on is: |
||||
|
||||
**For stateless components:** |
||||
|
||||
```tsx |
||||
export type MyNewButtonProps = { |
||||
label: string; |
||||
onClick: () => void; |
||||
}; |
||||
|
||||
export const MyNewButton: FC<MyNewButtonProps> = ({ label, onClick }) => ( |
||||
<button onClick={onCLick}>{label}</button> |
||||
); |
||||
``` |
||||
|
||||
**For stateful components:** |
||||
|
||||
```tsx |
||||
export type MyNewButtonProps = { |
||||
label: string; |
||||
onClick: () => void; |
||||
}; |
||||
|
||||
export const MyNewButton: FC<MyNewButtonProps> = ({ label, onClick }) => { |
||||
// do something, then call the onClick fn. e.g.: |
||||
const handleClick = useCallback(() => { |
||||
alert(label); |
||||
onClick && onClick(); |
||||
}, [label, onClick]); |
||||
|
||||
return <button onClick={onCLick}>{label}</button>; |
||||
}; |
||||
``` |
||||
|
||||
### Rationale |
||||
|
||||
Since there's a lot of ways to create components, settling on one pattern helps maintain readability. |
||||
But why _this_ style? |
||||
|
||||
See the discussion on the PR that introduced this pattern: [#2082](https://github.com/owncast/owncast/pull/2082). |
||||
|
||||
## Storybook |
||||
|
||||
We use [Storybook](https://storybook.js.org/) to create a component library where we can see and interact with each component. |
||||
|
||||
Make sure to include a `.stories.tsx` file with each (exported) component you create, and to update the stories file when making changes to existing components. |
||||
|
||||
You can run the Storybook server locally with `npm run storybook`. |
||||
|
||||
@ -0,0 +1,5 @@
@@ -0,0 +1,5 @@
|
||||
import { Canvas, Meta, Story } from '@storybook/addon-docs'; |
||||
|
||||
<Meta title="owncast/Documentation/{{title}}" /> |
||||
|
||||
{{content}} |
||||
@ -0,0 +1,34 @@
@@ -0,0 +1,34 @@
|
||||
import fs from 'fs'; |
||||
import handlebars from 'handlebars'; |
||||
|
||||
const template = fs.readFileSync('./Document.stories.mdx', 'utf8'); |
||||
let t = handlebars.compile(template, { noEscape: true }); |
||||
|
||||
const documents = [ |
||||
{ |
||||
title: 'Product Definition', |
||||
name: 'ProductDefinition', |
||||
path: '../../../docs/product-definition.md', |
||||
}, |
||||
{ title: 'Design', name: 'Design', path: '../../../.design/DESIGN.md' }, |
||||
{ |
||||
title: 'Building Frontend Components', |
||||
name: 'WebComponents', |
||||
path: '../../../web/components/_COMPONENT_HOW_TO.md', |
||||
}, |
||||
{ |
||||
title: 'Get Started with Owncast Development', |
||||
name: 'Development', |
||||
path: '/tmp/development.md', |
||||
}, |
||||
]; |
||||
|
||||
documents.forEach(doc => { |
||||
if (!fs.existsSync(doc.path)) { |
||||
return; |
||||
} |
||||
|
||||
const document = fs.readFileSync(doc.path, 'utf8'); |
||||
const output = t({ name: doc.name, title: doc.title, content: document }); |
||||
fs.writeFileSync(`../stories-category-doc-pages/${doc.name}.stories.mdx`, output); |
||||
}); |
||||
Loading…
Reference in new issue