{"_id":"@appicanis/better-docs","name":"@appicanis/better-docs","dist-tags":{"latest":"2.7.3"},"versions":{"2.7.3":{"name":"@appicanis/better-docs","version":"2.7.3","description":"JSdoc theme","repository":{"type":"git","url":"git+https://github.com/radozebra/better-docs.git"},"main":"publish.js","scripts":{"gulp":"gulp","test":"mocha --recursive './*spec.js' './typescript/*spec.js'","build":"babel src --out-dir lib","docs":"jsdoc -c jsdoc.json","release":"semantic-release"},"author":{"name":"Wojciech Krysiak"},"license":"MIT","dependencies":{"brace":"^0.11.1","react-ace":"^10.1.0","react-docgen":"^5.4.3","react-frame-component":"^5.2.6","typescript":"^4.9.5","underscore":"^1.13.6","vue-docgen-api":"^4.60.0","vue2-ace-editor":"^0.0.15"},"peerDependencies":{"react":"^17.0.2","react-dom":"^17.0.2"},"devDependencies":{"@babel/cli":"^7.21.0","@babel/core":"^7.21.0","@babel/preset-env":"^7.20.2","@babel/preset-react":"^7.18.6","@commitlint/cli":"^16.0.2","@commitlint/config-conventional":"^16.0.0","@semantic-release/git":"^10.0.1","@vue/component-compiler-utils":"^3.3.0","browser-sync":"^2.28.3","bulma":"^0.9.3","chai":"^4.3.4","eslint":"^8.7.0","eslint-config-airbnb":"^19.0.4","eslint-config-airbnb-base":"^15.0.0","eslint-plugin-import":"^2.25.4","eslint-plugin-jsx-a11y":"^6.5.1","eslint-plugin-react":"^7.28.0","eslint-plugin-vue":"^8.3.0","gulp":"^4.0.2","gulp-autoprefixer":"^8.0.0","gulp-babel":"^8.0.0","gulp-concat":"^2.6.1","gulp-rename":"^2.0.0","gulp-sass":"^5.1.0","gulp-uglify":"^3.0.2","husky":"^7.0.4","jsdoc":"^4.0.2","mocha":"^10.2.0","sass":"^1.48.0","semantic-release":"^20.1.1","taffydb":"^2.7.3","yarn-audit-fix":"^9.3.9"},"husky":{"hooks":{"commit-msg":"commitlint -E HUSKY_GIT_PARAMS"}},"engines":{"node":">=12"},"gitHead":"984a2c9c309688407ceaafbc5ad023af53276430","bugs":{"url":"https://github.com/radozebra/better-docs/issues"},"homepage":"https://github.com/radozebra/better-docs#readme","_id":"@appicanis/better-docs@2.7.3","_nodeVersion":"18.14.0","_npmVersion":"9.6.2","dist":{"integrity":"sha512-5B4xUVNHU7Xw8P+GeT3AzbB1VMIvwsnh3wGdN20jFp7HyrsOUO9XfLLa5XdG1fVNTBZrkBQliIvfcSBAJfNL0w==","shasum":"df57c2b083541b5b146559d561d47e341c17309b","tarball":"https://registry.npmjs.org/@appicanis/better-docs/-/better-docs-2.7.3.tgz","fileCount":157,"unpackedSize":1275641,"signatures":[{"keyid":"SHA256:jl3bwswu80PjjokCgh0o2w5c2U4LhQAE57gj9cz1kzA","sig":"MEUCIHRADzLScJ1T6/5M/zV8y5SqDQm/ULe5j20NZZarRpDCAiEAh+4q+pq1hbYzQVMWC8fesfemrysU8B3ohmj6NRQtS/Y="}]},"_npmUser":{"name":"appicanis","email":"radovan.obal@appicanis.si"},"directories":{},"maintainers":[{"name":"appicanis","email":"radovan.obal@appicanis.si"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages","tmp":"tmp/better-docs_2.7.3_1686147066230_0.7489120396992652"},"_hasShrinkwrap":false}},"time":{"created":"2023-06-07T14:11:06.157Z","2.7.3":"2023-06-07T14:11:06.602Z","modified":"2023-06-07T14:11:06.776Z"},"maintainers":[{"name":"appicanis","email":"radovan.obal@appicanis.si"}],"description":"JSdoc theme","homepage":"https://github.com/radozebra/better-docs#readme","repository":{"type":"git","url":"git+https://github.com/radozebra/better-docs.git"},"author":{"name":"Wojciech Krysiak"},"bugs":{"url":"https://github.com/radozebra/better-docs/issues"},"license":"MIT","readme":"<img src=\"./readme/logo.png\" />\r\n\r\nDocumentation toolbox for your **javascript** / **typescript** projects based on JSDoc3 with **@category**, **@component** and **@optional** plugins.\r\n\r\nThis is how it looks:\r\n\r\n<table>\r\n  <tr>\r\n    <td>\r\n      <a href='./readme/class.png'><img src=\"./readme/class.png\" style=\"width: 300px\"/></a>\r\n    </td>\r\n    <td>\r\n      <a href='./readme/with-mermaid.png'><img src=\"./readme/with-mermaid.png\" style=\"width: 300px\"/></a>\r\n    </td>\r\n    <td>\r\n      <a href='./readme/component.png'><img src=\"./readme/component.png\" style=\"width: 300px\"/></a>\r\n    </td>\r\n  </tr>\r\n</table>\r\n\r\n# Example\r\n\r\nExample documentation can be found here: https://softwarebrothers.github.io/example-design-system/index.html\r\n\r\n# OpenSource SoftwareBrothers community\r\n\r\n- [Join the community](https://join.slack.com/t/adminbro/shared_invite/zt-czfb79t1-0U7pn_KCqd5Ts~lbJK0_RA) to get help and be inspired.\r\n- subscribe to our [newsletter](http://opensource.softwarebrothers.co)\r\n\r\n# Installation\r\n\r\n```sh\r\nnpm install --save-dev better-docs\r\n```\r\n\r\n# Theme Usage\r\n\r\n## With command line\r\n\r\nAssuming that you have [jsdoc](https://github.com/jsdoc/jsdoc) installed globally:\r\n\r\n```\r\njsdoc your-documented-file.js -t ./node_modules/better-docs\r\n```\r\n\r\n## With npm and configuration file\r\n\r\nIn your projects package.json file - add a new script:\r\n\r\n```\r\n\"script\": {\r\n  \"docs\": \"jsdoc -c jsdoc.json\"\r\n}\r\n```\r\n\r\nin your `jsdoc.json` file, set the template:\r\n\r\n```json\r\n\"opts\": {\r\n  \"template\": \"node_modules/better-docs\"\r\n}\r\n```\r\n\r\n# TypeScript support\r\n\r\nbetter-docs has a plugin which allows you to generate documentation from your TypeScript codebase.\r\n\r\n## Usage\r\n\r\nTo use it update your `jsdoc.json` file\r\n\r\n```\r\n...\r\n\"tags\": {\r\n    \"allowUnknownTags\": [\"optional\"] //or true\r\n},\r\n\"plugins\": [\r\n    \"node_modules/better-docs/typescript\"\r\n],\r\n\"source\": {\r\n    \"includePattern\": \"\\\\.(jsx|js|ts|tsx)$\",\r\n},\r\n...\r\n```\r\n\r\nAnd now you can run your `jsdoc` command and parse TypeScript files.\r\n\r\n## How it works?\r\n\r\nIt performs 4 operations:\r\n\r\n* First of all it transpiles all .ts and .tsx files to .js, so that all comments used by you are treated\r\nas a regular JSDoc comments.\r\n\r\nFurthermore it:\r\n\r\n* Converts all your commented `type` aliases to `@typedef`\r\n* Converts all your commented `interface` definitions to `@interface`,\r\n* Converts descriptions for your public, protected, static class members\r\n\r\nso they can be printed by JSDoc automatically.\r\n\r\n## Examples\r\n\r\n```\r\n/**\r\n * ActionRequest\r\n * @memberof Action\r\n * @alias ActionRequest\r\n */\r\nexport type ActionRequest = {\r\n  /**\r\n   * parameters passed in an URL\r\n   */\r\n  params: {\r\n    /**\r\n     * Id of current resource\r\n     */\r\n    resourceId: string;\r\n    /**\r\n     * Id of current record\r\n     */\r\n    recordId?: string;\r\n    /**\r\n     * Name of an action\r\n     */\r\n    action: string;\r\n\r\n    [key: string]: any;\r\n  };\r\n}\r\n```\r\n\r\nis converted to:\r\n\r\n```\r\n/**\r\n * ActionRequest'\r\n * @memberof Action'\r\n * @alias ActionRequest'\r\n * @typedef {object} ActionRequest'\r\n * @property {object} params   parameters passed in an URL'\r\n * @property {string} params.resourceId   Id of current resource'\r\n * @property {string} [params.recordId]   Id of current record'\r\n * @property {string} params.action   Name of an action'\r\n * @property {any} params.{...}'\r\n */\r\n```\r\n\r\nAlso you can comment the interface in a similar fashion:\r\n\r\n```\r\n/**\r\n * JSON representation of an {@link Action}\r\n * @see Action\r\n */\r\nexport default interface ActionJSON {\r\n  /**\r\n   * Unique action name\r\n   */\r\n  name: string;\r\n  /**\r\n   * Type of an action\r\n   */\r\n  actionType: 'record' | 'resource' | Array<'record' | 'resource'>;\r\n  /**\r\n   * Action icon\r\n   */\r\n  icon?: string;\r\n  /**\r\n   * Action label - visible on the frontend\r\n   */\r\n  label: string;\r\n  /**\r\n   * Guarding message\r\n   */\r\n  guard?: string;\r\n  /**\r\n   * If action should have a filter (for resource actions)\r\n   */\r\n  showFilter: boolean;\r\n  /**\r\n   * Action component. When set to false action will be invoked immediately after clicking it,\r\n   * to put in another words: there wont be an action view\r\n   */\r\n  component?: string | false | null;\r\n}\r\n```\r\n\r\nor describe your class properties like that:\r\n\r\n```\r\n/**\r\n * Class name\r\n */\r\nclass ClassName {\r\n  /**\r\n   * Some private member which WONT be in jsdoc (because it is private)\r\n   */\r\n  private name: string\r\n\r\n  /**\r\n   * Some protected member which will go to the docs\r\n   */\r\n  protected somethingIsA: number\r\n\r\n  /**\r\n   * And static member which will goes to the docs.\r\n   */\r\n  static someStaticMember: number\r\n\r\n  public notCommentedWontBeInJSDoc: string\r\n\r\n  constructor(color: string) {}\r\n}\r\n```\r\n\r\n# @category plugin\r\n\r\nbetter-docs also allows you to nest your documentation into categories and subcategories in the sidebar menu.\r\n\r\n## Usage\r\n\r\nTo add a plugin - update `plugins` section in your `jsdoc.json` file:\r\n\r\n```\r\n...\r\n\"tags\": {\r\n    \"allowUnknownTags\": [\"category\"] //or true\r\n},\r\n\"plugins\": [\r\n    \"node_modules/better-docs/category\"\r\n],\r\n...\r\n```\r\n\r\nand then you can use `@category` and/or `@subcategory` tag in your code:\r\n\r\n```\r\n/**\r\n * Class description\r\n * @category Category\r\n * @subcategory All\r\n */\r\nclass YourClass {\r\n  ....\r\n}\r\n```\r\n\r\n# @component plugin [BETA]\r\n\r\nBetter-docs also allows you to document your [React](https://reactjs.org/) and [Vue](https://vuejs.org/) components automatically. The only thing you have to do is to add a `@component` tag. It will take all props from your components and along with an `@example` tag - will generate a __live preview__.\r\n\r\n## Installation instructions\r\n\r\nSimilar as before to add a plugin - you have to update the `plugins` section in your `jsdoc.json` file:\r\n\r\n```\r\n...\r\n\"tags\": {\r\n    \"allowUnknownTags\": [\"component\"] //or true\r\n},\r\n\"plugins\": [\r\n    \"node_modules/better-docs/component\"\r\n],\r\n...\r\n```\r\n\r\nSince __component__ plugin uses [parcel](https://parceljs.org) as a bundler you have to install it globally. To do this run:\r\n\r\n```\r\n# if you use npm\r\nnpm install -g parcel-bundler\r\n\r\n# or yarn\r\nyarn global add parcel-bundler\r\n```\r\n\r\n## Usage\r\n\r\nTo document components simply add `@component` in your JSDoc documentation:\r\n\r\n```jsx\r\n/**\r\n * Some documented component\r\n *\r\n * @component\r\n */\r\nconst Documented = (props) => {\r\n  const { text } = props\r\n  return (\r\n    <div>{text}</div>\r\n  )\r\n}\r\n\r\nDocumented.propTypes = {\r\n  /**\r\n   * Text is a text\r\n   */\r\n  text: PropTypes.string.isRequired,\r\n}\r\n\r\nexport default Documented\r\n```\r\n\r\nThe plugin will take the information from your [PropTypes](https://reactjs.org/docs/typechecking-with-proptypes.html) and put them into an array.\r\n\r\nFor Vue it looks similar:\r\n\r\n```vue\r\n<script>\r\n/**\r\n * @component\r\n */\r\nexport default {\r\n  name: 'ExampleComponent',\r\n  props: {\r\n    spent: {\r\n      type: Number,\r\n      default: 30,\r\n    },\r\n    remaining: {\r\n      type: Number,\r\n      default: 40,\r\n    }\r\n  },\r\n}\r\n</script>\r\n```\r\n\r\nIn this case, props will be taken from `props` property.\r\n\r\n## Preview\r\n\r\n`@component` plugin also modifies the behaviour of `@example` tag in a way that it can generate an actual __component preview__. What you have to do is to add an `@example` tag and return component from it:\r\n\r\n**React example:**\r\n\r\n```jsx\r\n/**\r\n * Some documented component\r\n *\r\n * @component\r\n * @example\r\n * const text = 'some example text'\r\n * return (\r\n *   <Documented text={text} />\r\n * )\r\n */\r\nconst Documented = (props) => {\r\n  ///...\r\n}\r\n```\r\n\r\n**Vue example 1:**\r\n\r\n```vue\r\n<script>\r\n/**\r\n * @component\r\n * @example\r\n * <ExampleComponent :spent=\"100\" :remaining=\"50\"></ExampleComponent>\r\n */\r\nexport default {\r\n  name: 'ExampleComponent',\r\n  //...\r\n}\r\n</script>\r\n```\r\n\r\n**Vue example 2:**\r\n\r\n```vue\r\n<script>\r\n/**\r\n * @component\r\n * @example\r\n * {\r\n *   template: `<Box>\r\n *     <ProgressBar :spent=\"spent\" :remaining=\"50\"></ProgressBar>\r\n *     <ProgressBar :spent=\"50\" :remaining=\"50\" style=\"margin-top: 20px\"></ProgressBar>\r\n *   </Box>`,\r\n *   data: function() {\r\n *     return {spent: 223};\r\n *   }\r\n * }\r\n */\r\nexport default {\r\n  name: 'ExampleComponent',\r\n  //...\r\n}\r\n</script>\r\n```\r\n\r\nYou can put as many `@example` tags as you like in one component and \"caption\" each of them like this:\r\n\r\n```javascript\r\n/**\r\n * @component\r\n * @example <caption>Example usage of method1.</caption>\r\n * // your example here\r\n */\r\n```\r\n\r\n## Mixing components in preview\r\n\r\nAlso you can use multiple components which are documented with `@component` tag together. So lets say you have 2 components and in the second component you want to use the first one as a wrapper like this:\r\n\r\n```javascript\r\n// component-1.js\r\n/**\r\n * Component 1\r\n * @component\r\n *\r\n */\r\nconst Component1 = (props) => {...}\r\n\r\n// component-2.js\r\n/**\r\n * Component 2\r\n * @component\r\n * @example\r\n * return (\r\n *   <Component1>\r\n *     <Component2 prop1={'some value'}/>\r\n *     <Component2 prop1={'some other value'}/>\r\n *   </Component1>\r\n * )\r\n */\r\nconst Component2 = (props) => {...}\r\n```\r\n\r\n## Wrapper component [only React]\r\n\r\nMost probably your components will have to be run within a particular context, like within redux store provider or with custom CSS libraries.\r\nYou can simulate this by passing a `component.wrapper` in your `jsdoc.json`:\r\n_(To read more about passing options - scroll down to __Customization__ section)_\r\n\r\n```json\r\n// jsdoc.json\r\n{\r\n    \"opts\": {...},\r\n    \"templates\": {\r\n        \"better-docs\": {\r\n            \"name\": \"Sample Documentation\",\r\n            \"component\": {\r\n              \"wrapper\": \"./path/to/your/wrapper-component.js\",\r\n            },\r\n            \"...\": \"...\",\r\n        }\r\n    }\r\n}\r\n```\r\n\r\nWrapper component can look like this:\r\n\r\n```javascript\r\n// wrapper-component.js\r\nimport React from 'react'\r\nimport { BrowserRouter } from 'react-router-dom'\r\nimport { createStore } from 'redux'\r\nimport { Provider } from 'react-redux'\r\n\r\nconst store = createStore(() => ({}), {})\r\n\r\nconst Component = (props) => {\r\n  return (\r\n    <React.Fragment>\r\n      <head>\r\n        <link type=\"text/css\" rel=\"stylesheet\" href=\"https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.5/css/bulma.css\" />\r\n      </head>\r\n      <Provider store={store}>\r\n        <BrowserRouter>\r\n          {props.children}\r\n        </BrowserRouter>\r\n      </Provider>\r\n    </React.Fragment>\r\n  )\r\n}\r\n\r\nexport default Component\r\n```\r\n\r\n## Styling React examples\r\n\r\nBetter-docs inserts all examples within an `iframe`. This results in the following styling options:\r\n\r\n1. If you pass styles inline - they will work right away.\r\n\r\n2. For `css modules` to work with `parcel` bundler - you have to install `postcss-modules` package:\r\n\r\n```\r\nyarn add postcss-modules\r\n```\r\n\r\nand create a `.postcssrc` file:\r\n\r\n\r\n```json\r\n// .postcssrc\r\n{\r\n\t\"modules\": true\r\n}\r\n```\r\n\r\n3. For [styled-components](https://www.styled-components.com/) you have to use wrapper component which looks like this:\r\n\r\n```jsx\r\nimport React from 'react'\r\nimport { StyleSheetManager } from 'styled-components'\r\n\r\nconst Component = (props) => {\r\n  const { frameContext } = props\r\n  return (\r\n    <StyleSheetManager target={frameContext.document.head}>\r\n      {props.children}\r\n    </StyleSheetManager>\r\n  )\r\n}\r\n\r\nexport default Component\r\n```\r\n\r\n## Adding commands to bundle entry file\r\n\r\n`@component` plugin creates an entry file: `.entry.js` in your _docs_ output folder. Sometimes you might want to add something to it. You can do this by passing: `component.entry` option, which is an array of strings.\r\n\r\nSo let's say you want to add `babel-polyfill` and 'bulma.css' framework to your bundle. You can do it like this:\r\n\r\n```json\r\n// jsdoc.json\r\n{\r\n    \"opts\": {...},\r\n    \"templates\": {\r\n        \"better-docs\": {\r\n            \"name\": \"Sample Documentation\",\r\n            \"component\": {\r\n                \"entry\": [\r\n                    \"import 'babel-polyfill';\",\r\n                    \"import 'bulma/css/bulma.css';\"\r\n                ]\r\n            },\r\n            \"...\": \"...\",\r\n        }\r\n    }\r\n}\r\n```\r\n\r\n# Customization\r\n\r\nFirst of all, let me state that better-docs extends the `default` template. That is why default template parameters are also handled.\r\n\r\n[BETA]: You must explicitly set the `search` option of the `default` template to `true` to enable search\r\n\r\nTo customize the better-docs pass `options` to `templates['better-docs']`. section in your `jsdoc configuration file`.\r\n\r\nExample configuration file with settings for both `default` and `better-docs` templates:\r\n\r\n```json\r\n{\r\n    \"tags\": {\r\n        \"allowUnknownTags\": [\"category\"]\r\n    },\r\n    \"source\": {\r\n        \"include\": [\"./src\"],\r\n        \"includePattern\": \".js$\",\r\n        \"excludePattern\": \"(node_modules/|docs)\"\r\n    },\r\n    \"plugins\": [\r\n        \"plugins/markdown\",\r\n        \"jsdoc-mermaid\",\r\n        \"node_modules/better-docs/category\"\r\n    ],\r\n    \"opts\": {\r\n        \"encoding\": \"utf8\",\r\n        \"destination\": \"docs/\",\r\n        \"readme\": \"readme.md\",\r\n        \"recurse\": true,\r\n        \"verbose\": true,\r\n        \"tutorials\": \"./docs-src/tutorials\",\r\n        \"template\": \"better-docs\"\r\n    },\r\n    \"templates\": {\r\n        \"cleverLinks\": false,\r\n        \"monospaceLinks\": false,\r\n        \"search\": true,\r\n        \"default\": {\r\n            \"staticFiles\": {\r\n              \"include\": [\r\n                  \"./docs-src/statics\"\r\n              ]\r\n            }\r\n        },\r\n        \"better-docs\": {\r\n            \"name\": \"Sample Documentation\",\r\n            \"logo\": \"images/logo.png\",\r\n            \"title\": \"\", // HTML title\r\n            \"css\": \"style.css\",\r\n            \"trackingCode\": \"tracking-code-which-will-go-to-the-HEAD\",\r\n\t    \"hideGenerator\": false,\r\n            \"navLinks\": [\r\n                {\r\n                    \"label\": \"Github\",\r\n                    \"href\": \"https://github.com/SoftwareBrothers/admin-bro\"\r\n                },\r\n                {\r\n                    \"label\": \"Example Application\",\r\n                    \"href\": \"https://admin-bro-example-app-staging.herokuapp.com/admin\"\r\n                }\r\n            ]\r\n        }\r\n    }\r\n}\r\n```\r\n\r\n## Extras\r\n\r\n### typedef(import(...))\r\n\r\nbetter-docs also has one extra plugin for handling typescript'like types imports like (it has to be one-liner):\r\n\r\n```\r\n/** @typedef {import('./some-other-file').ExportedType} ExportedType */\r\n```\r\n\r\nIt simply removes that from the code so JSDoc wont throw an error. In order to use it add this plugin to your plugins section:\r\n\r\n```\r\n  \"plugins\": [\r\n        \"node_modules/better-docs/typedef-import\"\r\n    ],\r\n```\r\n\r\n# Setting up for the development\r\n\r\nIf you want to change the theme locally follow the steps:\r\n\r\n1. Clone the repo to the folder where you have the project:\r\n\r\n```\r\ncd your-project\r\ngit clone git@github.com:SoftwareBrothers/better-docs.git\r\n```\r\n\r\nor add it as a git submodule:\r\n\r\n```\r\ngit submodule add git@github.com:SoftwareBrothers/better-docs.git\r\n```\r\n\r\n2. Install the packages\r\n\r\n```\r\ncd better-docs\r\n\r\nnpm install\r\n\r\n# or\r\n\r\nyarn\r\n```\r\n\r\n3. Within the better-docs folder run the gulp script. It will regenerate documentation every time you change something.\r\n\r\nIt supports following EVN variables:\r\n\r\n* `DOCS_COMMAND` - a command in your root repo which you use to generate documentation: i.e. `DOCS_COMMAND='jsdoc -c jsdoc.json'` or `npm run docs` if you have `docs` command defined in `package.json` file\r\n* `DOCS_OUTPUT` - where your documentation is generated. It should point to the same folder your jsdoc `--destination` conf. But make sure that it is relative to the path where you cloned `better-docs`.\r\n* `DOCS` - list of folders from your original repo what you want to watch for changes. Separated by comma.\r\n\r\n```\r\ncd better-docs\r\nDOCS_COMMAND='npm run docs' DOCS=../src/**/*,../config/**/* DOCS_OUTPUT=../docs cd better-docs && gulp\r\n```\r\n\r\nThe script should launch the browser and refresh it whenever you change something in the template or in `DOCS`.\r\n\r\n# Setting up the jsdoc in your project\r\n\r\nIf you want to see how to setup jsdoc in your project - take a look at these brief tutorials:\r\n\r\n- JSDoc - https://www.youtube.com/watch?v=Yl6WARA3IhQ\r\n- better-docs and Mermaid: https://www.youtube.com/watch?v=UBMYogTzsBk\r\n\r\n# License\r\n\r\nbetter-docs is Copyright © 2019 SoftwareBrothers.co. It is free software and may be redistributed under the terms specified in the [LICENSE](LICENSE) file - MIT.\r\n\r\n# About SoftwareBrothers.co\r\n\r\n<img src=\"https://softwarebrothers.co/assets/images/software-brothers-logo-full.svg\" width=240>\r\n\r\n\r\nWe're an open, friendly team that helps clients from all over the world to transform their businesses and create astonishing products.\r\n\r\n* We are available for [hire](https://softwarebrothers.co/contact).\r\n* If you want to work for us - check out the [career page](https://softwarebrothers.co/career).\r\n","readmeFilename":"README.md"}