├── doc └── releases │ ├── v1.5.14.md │ ├── v1.7.2.md │ ├── v1.7.6.md │ ├── v1.5.38.md │ ├── v1.5.10.md │ ├── v1.5.34.md │ ├── v1.5.2.md │ ├── v1.5.12.md │ ├── v1.5.30.md │ ├── v1.5.26.md │ ├── v1.5.24.md │ ├── v1.5.28.md │ ├── v1.7.4.md │ ├── v1.5.16.md │ ├── v1.5.22.md │ ├── v1.5.32.md │ ├── v1.5.20.md │ ├── v1.5.36.md │ ├── v1.5.6.md │ └── v1.6.0.md ├── src ├── views │ ├── CopyZone │ │ ├── CopyZone.css │ │ ├── index.js │ │ ├── CopyZone.js │ │ └── CopyZone.stories.js │ ├── DiagramType │ │ ├── DiagramType.css │ │ ├── index.js │ │ ├── DiagramType.stories.js │ │ └── DiagramType.js │ ├── ExampleCards │ │ ├── ExampleCards.css │ │ ├── ExampleCards.js │ │ ├── ExampleCards.stories.js │ │ └── index.js │ ├── WindowImportUrl │ │ ├── WindowImportUrl.css │ │ ├── index.js │ │ ├── WindowImportUrl.js │ │ └── WindowImportUrl.stories.js │ ├── Router │ │ ├── index.js │ │ ├── index.css │ │ └── Router.js │ ├── Window │ │ ├── index.js │ │ ├── Window.css │ │ ├── Window.js │ │ └── Window.stories.js │ ├── WindowExampleDetail │ │ ├── WindowExampleDetail.css │ │ ├── index.js │ │ ├── WindowExampleDetail.stories.js │ │ └── WindowExampleDetail.js │ ├── Title │ │ ├── index.js │ │ ├── Title.js │ │ ├── Title.css │ │ └── Title.stories.js │ ├── Columns │ │ ├── index.js │ │ ├── Columns.css │ │ ├── Columns.stories.js │ │ └── Columns.js │ ├── ExampleCard │ │ ├── index.js │ │ ├── ExampleCard.css │ │ ├── ExampleCard.stories.js │ │ └── ExampleCard.js │ ├── SubTitle │ │ ├── index.js │ │ ├── SubTitle.css │ │ ├── SubTitle.stories.js │ │ └── SubTitle.js │ ├── ShrinkableButton │ │ ├── index.js │ │ ├── ShrinkableButton.stories.js │ │ ├── ShrinkableButton.css │ │ └── ShrinkableButton.js │ ├── fomantic-ui-css │ │ └── themes │ │ │ ├── basic │ │ │ └── assets │ │ │ │ └── fonts │ │ │ │ ├── icons.eot │ │ │ │ ├── icons.ttf │ │ │ │ └── icons.woff │ │ │ ├── default │ │ │ └── assets │ │ │ │ ├── fonts │ │ │ │ ├── icons.eot │ │ │ │ ├── icons.ttf │ │ │ │ ├── icons.woff │ │ │ │ ├── icons.woff2 │ │ │ │ ├── brand-icons.eot │ │ │ │ ├── brand-icons.ttf │ │ │ │ ├── brand-icons.woff │ │ │ │ ├── brand-icons.woff2 │ │ │ │ ├── outline-icons.eot │ │ │ │ ├── outline-icons.ttf │ │ │ │ ├── outline-icons.woff │ │ │ │ └── outline-icons.woff2 │ │ │ │ └── images │ │ │ │ └── flags.png │ │ │ ├── github │ │ │ └── assets │ │ │ │ └── fonts │ │ │ │ ├── octicons.ttf │ │ │ │ ├── octicons.woff │ │ │ │ └── octicons-local.ttf │ │ │ └── material │ │ │ └── assets │ │ │ └── fonts │ │ │ ├── icons.eot │ │ │ ├── icons.ttf │ │ │ ├── icons.woff │ │ │ └── icons.woff2 │ ├── storybook │ │ ├── stories.js │ │ └── store.js │ ├── ExampleDetail │ │ ├── ExampleDetail.css │ │ ├── index.js │ │ ├── ExampleDetail.stories.js │ │ └── ExampleDetail.js │ ├── WindowExampleCards │ │ ├── WindowExampleCards.css │ │ ├── index.js │ │ ├── WindowExampleCards.stories.js │ │ └── WindowExampleCards.js │ ├── CopyField │ │ ├── CopyField.css │ │ ├── index.js │ │ ├── CopyField.stories.js │ │ └── CopyField.js │ ├── RenderUrl │ │ ├── RenderUrl.stories.js │ │ ├── index.js │ │ └── RenderUrl.js │ ├── Editor │ │ ├── index.js │ │ ├── Editor.stories.js │ │ ├── Editor.css │ │ └── Editor.js │ ├── Render │ │ ├── Render.css │ │ ├── index.js │ │ └── Render.js │ └── App │ │ ├── index.js │ │ ├── App.css │ │ ├── App.stories.js │ │ └── App.js ├── actions │ ├── __jest__ │ │ ├── time.js │ │ ├── copy.js │ │ └── thunk.js │ ├── index.js │ ├── utils │ │ └── delay.js │ ├── example.js │ ├── example.test.js │ ├── editor.js │ └── editor.test.js ├── debug.js ├── kroki │ ├── utils.js │ ├── krokiInfo.js │ ├── coder.js │ └── coder.test.js ├── setupTests.js ├── providers │ ├── exportToWindow.js │ ├── debug.js │ └── diagramChange.js ├── reducers │ ├── debug.js │ ├── index.js │ ├── utils │ │ ├── analytics_functions.js │ │ ├── createReducer.js │ │ └── analytics_functions.test.js │ ├── niolesk.js │ ├── example.js │ └── editor.js ├── constants │ ├── example.js │ └── editor.js ├── reportWebVitals.js ├── examples │ └── usecache.js ├── index.js └── init │ ├── react.js │ ├── redux.js │ └── reactRedux.js ├── res ├── niolesk.ico ├── niolesk-16.png ├── niolesk-24.png ├── niolesk-32.png ├── niolesk-48.png ├── niolesk-64.png ├── niolesk-128.png ├── niolesk-192.png ├── niolesk-256.png ├── niolesk-512.png └── niolesk.svg ├── .env ├── public ├── favicon.ico ├── logo192.png ├── logo512.png ├── robots.txt ├── config.js ├── manifest.json ├── index.html └── config-analytic-providers.js ├── webpack.config.js ├── _templates ├── doc │ └── release │ │ └── release.ejs.t ├── view │ ├── comp │ │ ├── css.ejs.t │ │ ├── index.ejs.t │ │ ├── prompt.js │ │ ├── component.ejs.t │ │ └── stories.ejs.t │ └── cont │ │ ├── css.ejs.t │ │ ├── prompt.js │ │ ├── component.ejs.t │ │ ├── index.ejs.t │ │ └── stories.ejs.t ├── action │ └── new │ │ ├── constant.ejs.t │ │ └── action.ejs.t └── reducer │ └── new │ └── reducer.ejs.t ├── .editorconfig ├── .storybook ├── preview.js └── main.js ├── .gitpod.Dockerfile ├── docker-compose-example └── docker-compose.yml ├── .yarnrc.yml ├── .gitpod.yml ├── .github └── workflows │ ├── version.yml │ ├── deploy.yml │ └── build.yml ├── .gitignore ├── LICENSE ├── Dockerfile-for-local-build ├── Makefile ├── create-example-cache.js ├── Dockerfile ├── docker-res └── update-config.sh ├── package.json ├── compilation.md └── README.md /doc/releases/v1.5.14.md: -------------------------------------------------------------------------------- 1 | * Fix bytefield's doc URL 2 | -------------------------------------------------------------------------------- /src/views/CopyZone/CopyZone.css: -------------------------------------------------------------------------------- 1 | .CopyZone { 2 | } 3 | -------------------------------------------------------------------------------- /doc/releases/v1.7.2.md: -------------------------------------------------------------------------------- 1 | * FEATURE : Support TikZ diagrams -------------------------------------------------------------------------------- /src/views/DiagramType/DiagramType.css: -------------------------------------------------------------------------------- 1 | .DiagramType { 2 | } 3 | -------------------------------------------------------------------------------- /doc/releases/v1.7.6.md: -------------------------------------------------------------------------------- 1 | * FEATURE : Add a mindmap example for TikZ -------------------------------------------------------------------------------- /src/views/ExampleCards/ExampleCards.css: -------------------------------------------------------------------------------- 1 | .ExampleCards { 2 | } 3 | -------------------------------------------------------------------------------- /src/views/WindowImportUrl/WindowImportUrl.css: -------------------------------------------------------------------------------- 1 | .WindowImportUrl { 2 | } 3 | -------------------------------------------------------------------------------- /doc/releases/v1.5.38.md: -------------------------------------------------------------------------------- 1 | * DOC : Forgot to release the project under MIT License 2 | -------------------------------------------------------------------------------- /res/niolesk.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/res/niolesk.ico -------------------------------------------------------------------------------- /.env: -------------------------------------------------------------------------------- 1 | REACT_APP_VERSION=$npm_package_version 2 | REACT_APP_NAME=$npm_package_name 3 | -------------------------------------------------------------------------------- /doc/releases/v1.5.10.md: -------------------------------------------------------------------------------- 1 | * Reformat Vega/Vega-lite examples to indent them correctly 2 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/public/favicon.ico -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/public/logo512.png -------------------------------------------------------------------------------- /res/niolesk-16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/res/niolesk-16.png -------------------------------------------------------------------------------- /res/niolesk-24.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/res/niolesk-24.png -------------------------------------------------------------------------------- /res/niolesk-32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/res/niolesk-32.png -------------------------------------------------------------------------------- /res/niolesk-48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/res/niolesk-48.png -------------------------------------------------------------------------------- /res/niolesk-64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/res/niolesk-64.png -------------------------------------------------------------------------------- /src/views/Router/index.js: -------------------------------------------------------------------------------- 1 | import Router from './Router'; 2 | 3 | export default Router; -------------------------------------------------------------------------------- /src/views/Window/index.js: -------------------------------------------------------------------------------- 1 | import Window from './Window' 2 | 3 | export default Window; -------------------------------------------------------------------------------- /src/views/WindowExampleDetail/WindowExampleDetail.css: -------------------------------------------------------------------------------- 1 | .WindowExampleDetail { 2 | } 3 | -------------------------------------------------------------------------------- /webpack.config.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/webpack.config.js -------------------------------------------------------------------------------- /doc/releases/v1.5.34.md: -------------------------------------------------------------------------------- 1 | FEATURE : Support WireViz diagrams and examples (with doc links) 2 | -------------------------------------------------------------------------------- /res/niolesk-128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/res/niolesk-128.png -------------------------------------------------------------------------------- /res/niolesk-192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/res/niolesk-192.png -------------------------------------------------------------------------------- /res/niolesk-256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/res/niolesk-256.png -------------------------------------------------------------------------------- /res/niolesk-512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/res/niolesk-512.png -------------------------------------------------------------------------------- /src/actions/__jest__/time.js: -------------------------------------------------------------------------------- 1 | export const getCurrentTime = () => (new Date()).getTime(); 2 | -------------------------------------------------------------------------------- /src/views/Title/index.js: -------------------------------------------------------------------------------- 1 | import Title from './Title'; 2 | 3 | export default Title; 4 | 5 | -------------------------------------------------------------------------------- /_templates/doc/release/release.ejs.t: -------------------------------------------------------------------------------- 1 | --- 2 | to: doc/releases/v<%= name %>.md 3 | --- 4 | * 5 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: / 4 | -------------------------------------------------------------------------------- /src/debug.js: -------------------------------------------------------------------------------- 1 | import actions from './actions' 2 | 3 | window.actions = actions 4 | 5 | export {} -------------------------------------------------------------------------------- /doc/releases/v1.5.2.md: -------------------------------------------------------------------------------- 1 | * Create a Zen mode edition [#25](https://github.com/webgiss/niolesk/issues/25) -------------------------------------------------------------------------------- /src/views/Columns/index.js: -------------------------------------------------------------------------------- 1 | import Columns from './Columns'; 2 | 3 | export default Columns; 4 | 5 | -------------------------------------------------------------------------------- /src/views/CopyZone/index.js: -------------------------------------------------------------------------------- 1 | import CopyZone from './CopyZone'; 2 | 3 | export default CopyZone; 4 | -------------------------------------------------------------------------------- /doc/releases/v1.5.12.md: -------------------------------------------------------------------------------- 1 | * Make niolesk robust to half kroki url provided like https://kroki.io/c4plantuml/ -------------------------------------------------------------------------------- /src/views/ExampleCard/index.js: -------------------------------------------------------------------------------- 1 | import ExampleCard from './ExampleCard' 2 | 3 | export default ExampleCard; -------------------------------------------------------------------------------- /src/views/SubTitle/index.js: -------------------------------------------------------------------------------- 1 | import SubTitle from './SubTitle'; 2 | 3 | export default SubTitle; 4 | 5 | -------------------------------------------------------------------------------- /_templates/view/comp/css.ejs.t: -------------------------------------------------------------------------------- 1 | --- 2 | to: src/views/<%= name %>/<%= name %>.css 3 | --- 4 | .<%= name %> { 5 | } 6 | -------------------------------------------------------------------------------- /_templates/view/cont/css.ejs.t: -------------------------------------------------------------------------------- 1 | --- 2 | to: src/views/<%= name %>/<%= name %>.css 3 | --- 4 | .<%= name %> { 5 | } 6 | -------------------------------------------------------------------------------- /doc/releases/v1.5.30.md: -------------------------------------------------------------------------------- 1 | * FEATURE : Build docker image for amd64, arm64, 386, arm/v7 2 | * FEATURE : Adding pan-zoom support 3 | -------------------------------------------------------------------------------- /doc/releases/v1.5.26.md: -------------------------------------------------------------------------------- 1 | * FEATURE : Add support for D2 (+examples) 2 | * FEATURE : Add DBML support 3 | * FEATURE : Add Structurizr -------------------------------------------------------------------------------- /doc/releases/v1.5.24.md: -------------------------------------------------------------------------------- 1 | * Fixing typos [#32](https://github.com/webgiss/niolesk/issues/32) (From [deining](https://github.com/deining)) -------------------------------------------------------------------------------- /doc/releases/v1.5.28.md: -------------------------------------------------------------------------------- 1 | * FEATURE : Add support for D2 (+examples) 2 | * FEATURE : Add DBML support 3 | * FEATURE : Add Structurizr 4 | -------------------------------------------------------------------------------- /src/views/ShrinkableButton/index.js: -------------------------------------------------------------------------------- 1 | import ShrinkableButton from './ShrinkableButton'; 2 | 3 | export default ShrinkableButton; 4 | 5 | -------------------------------------------------------------------------------- /doc/releases/v1.7.4.md: -------------------------------------------------------------------------------- 1 | * FIX : Fix case for 'UMLet' ([Andreas Deininger](https://github.com/deining)) 2 | * FEATURE : Add support for analytics -------------------------------------------------------------------------------- /doc/releases/v1.5.16.md: -------------------------------------------------------------------------------- 1 | * CI : Add a github action job to create a release 2 | * FEATURE : Update default plantuml examples to include skin "rose" 3 | -------------------------------------------------------------------------------- /doc/releases/v1.5.22.md: -------------------------------------------------------------------------------- 1 | * FEATURE : Add a "Copy" section "Markdown content with source as comment [#28](https://github.com/webgiss/niolesk/issues/28) 2 | -------------------------------------------------------------------------------- /doc/releases/v1.5.32.md: -------------------------------------------------------------------------------- 1 | * FEATURE : Provide images than can start as random user id 2 | * BUGFIX : Make the diagram selectable for copy/paste (regression from pan & zoom) -------------------------------------------------------------------------------- /src/actions/index.js: -------------------------------------------------------------------------------- 1 | import * as editor from './editor' 2 | import * as example from './example' 3 | 4 | const actions = { editor, example } 5 | 6 | export default actions -------------------------------------------------------------------------------- /src/views/fomantic-ui-css/themes/basic/assets/fonts/icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/src/views/fomantic-ui-css/themes/basic/assets/fonts/icons.eot -------------------------------------------------------------------------------- /src/views/fomantic-ui-css/themes/basic/assets/fonts/icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/src/views/fomantic-ui-css/themes/basic/assets/fonts/icons.ttf -------------------------------------------------------------------------------- /_templates/view/comp/index.ejs.t: -------------------------------------------------------------------------------- 1 | --- 2 | to: src/views/<%= name %>/index.js 3 | --- 4 | import <%= name %> from './<%= name %>'; 5 | 6 | export default <%= name %>; 7 | 8 | -------------------------------------------------------------------------------- /src/views/fomantic-ui-css/themes/basic/assets/fonts/icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/src/views/fomantic-ui-css/themes/basic/assets/fonts/icons.woff -------------------------------------------------------------------------------- /src/views/fomantic-ui-css/themes/default/assets/fonts/icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/src/views/fomantic-ui-css/themes/default/assets/fonts/icons.eot -------------------------------------------------------------------------------- /src/views/fomantic-ui-css/themes/default/assets/fonts/icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/src/views/fomantic-ui-css/themes/default/assets/fonts/icons.ttf -------------------------------------------------------------------------------- /src/kroki/utils.js: -------------------------------------------------------------------------------- 1 | export const createKrokiUrl = (renderUrl, diagramType, filetype, encodedText) => [renderUrl.replace(/\/*$/, ''), diagramType, filetype, encodedText].join('/') 2 | -------------------------------------------------------------------------------- /src/views/fomantic-ui-css/themes/default/assets/fonts/icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/src/views/fomantic-ui-css/themes/default/assets/fonts/icons.woff -------------------------------------------------------------------------------- /src/views/fomantic-ui-css/themes/default/assets/fonts/icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/src/views/fomantic-ui-css/themes/default/assets/fonts/icons.woff2 -------------------------------------------------------------------------------- /src/views/fomantic-ui-css/themes/default/assets/images/flags.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/src/views/fomantic-ui-css/themes/default/assets/images/flags.png -------------------------------------------------------------------------------- /src/views/fomantic-ui-css/themes/github/assets/fonts/octicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/src/views/fomantic-ui-css/themes/github/assets/fonts/octicons.ttf -------------------------------------------------------------------------------- /src/views/fomantic-ui-css/themes/github/assets/fonts/octicons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/src/views/fomantic-ui-css/themes/github/assets/fonts/octicons.woff -------------------------------------------------------------------------------- /src/views/fomantic-ui-css/themes/material/assets/fonts/icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/src/views/fomantic-ui-css/themes/material/assets/fonts/icons.eot -------------------------------------------------------------------------------- /src/views/fomantic-ui-css/themes/material/assets/fonts/icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/src/views/fomantic-ui-css/themes/material/assets/fonts/icons.ttf -------------------------------------------------------------------------------- /src/views/fomantic-ui-css/themes/material/assets/fonts/icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/src/views/fomantic-ui-css/themes/material/assets/fonts/icons.woff -------------------------------------------------------------------------------- /src/views/fomantic-ui-css/themes/material/assets/fonts/icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/src/views/fomantic-ui-css/themes/material/assets/fonts/icons.woff2 -------------------------------------------------------------------------------- /_templates/action/new/constant.ejs.t: -------------------------------------------------------------------------------- 1 | --- 2 | to: src/constants/<%= name %>.js 3 | --- 4 | export const NEW_ACTION = 'NEW_ACTION'; 5 | export const ANOTHER_ACTION = 'ANOTHER_ACTION'; 6 | -------------------------------------------------------------------------------- /src/views/fomantic-ui-css/themes/default/assets/fonts/brand-icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/src/views/fomantic-ui-css/themes/default/assets/fonts/brand-icons.eot -------------------------------------------------------------------------------- /src/views/fomantic-ui-css/themes/default/assets/fonts/brand-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/src/views/fomantic-ui-css/themes/default/assets/fonts/brand-icons.ttf -------------------------------------------------------------------------------- /src/views/fomantic-ui-css/themes/default/assets/fonts/brand-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/src/views/fomantic-ui-css/themes/default/assets/fonts/brand-icons.woff -------------------------------------------------------------------------------- /src/views/fomantic-ui-css/themes/default/assets/fonts/brand-icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/src/views/fomantic-ui-css/themes/default/assets/fonts/brand-icons.woff2 -------------------------------------------------------------------------------- /src/views/fomantic-ui-css/themes/default/assets/fonts/outline-icons.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/src/views/fomantic-ui-css/themes/default/assets/fonts/outline-icons.eot -------------------------------------------------------------------------------- /src/views/fomantic-ui-css/themes/default/assets/fonts/outline-icons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/src/views/fomantic-ui-css/themes/default/assets/fonts/outline-icons.ttf -------------------------------------------------------------------------------- /src/views/fomantic-ui-css/themes/github/assets/fonts/octicons-local.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/src/views/fomantic-ui-css/themes/github/assets/fonts/octicons-local.ttf -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | end_of_line = lf 5 | insert_final_newline = true 6 | 7 | [*.{js,json,yml}] 8 | charset = utf-8 9 | indent_style = space 10 | indent_size = 4 11 | -------------------------------------------------------------------------------- /src/views/SubTitle/SubTitle.css: -------------------------------------------------------------------------------- 1 | .SubTitle { 2 | width: 100%; 3 | font-size: 1.45rem !important; 4 | text-align: right; 5 | } 6 | 7 | .SubTitleSmall { 8 | font-size: 0.68rem; 9 | } -------------------------------------------------------------------------------- /src/views/fomantic-ui-css/themes/default/assets/fonts/outline-icons.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/src/views/fomantic-ui-css/themes/default/assets/fonts/outline-icons.woff -------------------------------------------------------------------------------- /src/views/fomantic-ui-css/themes/default/assets/fonts/outline-icons.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webgiss/niolesk/HEAD/src/views/fomantic-ui-css/themes/default/assets/fonts/outline-icons.woff2 -------------------------------------------------------------------------------- /src/views/storybook/stories.js: -------------------------------------------------------------------------------- 1 | export const getComponenent = (Template, args) =>{ 2 | const Component = Template.bind({}); 3 | Component.args = args; 4 | return Component; 5 | } 6 | -------------------------------------------------------------------------------- /doc/releases/v1.5.20.md: -------------------------------------------------------------------------------- 1 | * BUGFIX : Fix broken PlantUML example ([Thomas Wucher](https://github.com/thomaswucher)) 2 | * FEATURE : Add UMlet example ([Thomas Wucher](https://github.com/thomaswucher)) 3 | -------------------------------------------------------------------------------- /doc/releases/v1.5.36.md: -------------------------------------------------------------------------------- 1 | * FEATURE: Better zen mode on small screens (when editor is on top of diagram, the editor takes only half of the height of the screen) 2 | * STORYBOOK: Fix some storybook screens -------------------------------------------------------------------------------- /doc/releases/v1.5.6.md: -------------------------------------------------------------------------------- 1 | * Add shortcut Alt+z to open Zen Mode 2 | * Add shortcut Alt+i to open Import URL window 3 | * Add shortcut Alt+x to open Examples window 4 | * Add release description based on doc/releases folder 5 | -------------------------------------------------------------------------------- /.storybook/preview.js: -------------------------------------------------------------------------------- 1 | export const parameters = { 2 | actions: { argTypesRegex: "^on[A-Z].*" }, 3 | controls: { 4 | matchers: { 5 | color: /(background|color)$/i, 6 | date: /Date$/, 7 | }, 8 | }, 9 | } -------------------------------------------------------------------------------- /src/views/ExampleDetail/ExampleDetail.css: -------------------------------------------------------------------------------- 1 | .ExampleDetail { 2 | } 3 | 4 | .ExampleDetailCode { 5 | text-align: left; 6 | overflow-x: auto; 7 | } 8 | 9 | .ui.segment.ExampleDetailCodeSegment { 10 | background-color: #ffffec; 11 | } -------------------------------------------------------------------------------- /_templates/reducer/new/reducer.ejs.t: -------------------------------------------------------------------------------- 1 | --- 2 | to: src/reducers/<%= name %>.js 3 | --- 4 | import { createReducer } from "./utils/createReducer"; 5 | 6 | const initialState = { 7 | }; 8 | 9 | export default createReducer({ 10 | 11 | }, initialState); 12 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | -------------------------------------------------------------------------------- /src/views/Title/Title.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Header } from 'semantic-ui-react' 3 | 4 | import './Title.css' 5 | 6 | const Title = () => { 7 | return
Niolesk
8 | } 9 | 10 | export default Title; 11 | -------------------------------------------------------------------------------- /_templates/view/comp/prompt.js: -------------------------------------------------------------------------------- 1 | // see types of prompts: 2 | // https://github.com/enquirer/enquirer/tree/master/examples 3 | // 4 | module.exports = [ 5 | { 6 | type: 'input', 7 | name: 'name', 8 | message: "What's your component name ?" 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /_templates/view/cont/prompt.js: -------------------------------------------------------------------------------- 1 | // see types of prompts: 2 | // https://github.com/enquirer/enquirer/tree/master/examples 3 | // 4 | module.exports = [ 5 | { 6 | type: 'input', 7 | name: 'name', 8 | message: "What's your component name ?" 9 | } 10 | ] 11 | -------------------------------------------------------------------------------- /src/views/Router/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | -webkit-font-smoothing: antialiased; 3 | -moz-osx-font-smoothing: grayscale; 4 | } 5 | 6 | a:hover { 7 | color: #363636; 8 | } 9 | 10 | a { 11 | color: #3273dc; 12 | cursor: pointer; 13 | text-decoration: none; 14 | } -------------------------------------------------------------------------------- /.gitpod.Dockerfile: -------------------------------------------------------------------------------- 1 | FROM gitpod/workspace-full:latest 2 | 3 | RUN bash -c 'VERSION="16" \ 4 | && source $HOME/.nvm/nvm.sh && nvm install $VERSION \ 5 | && nvm use $VERSION && nvm alias default $VERSION' 6 | 7 | RUN echo "nvm use default &>/dev/null" >> ~/.bashrc.d/51-nvm-fix 8 | -------------------------------------------------------------------------------- /src/providers/exportToWindow.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @type {import('../init/reactRedux').Provider} 3 | * @template State 4 | */ 5 | const provider = { 6 | onNewState: (state) => { 7 | window.state = state; 8 | } 9 | }; 10 | 11 | export default provider; 12 | -------------------------------------------------------------------------------- /src/views/Title/Title.css: -------------------------------------------------------------------------------- 1 | .Title { 2 | text-align: center; 3 | font-size: 3rem !important; 4 | font-family: BlinkMacSystemFont,-apple-system,"Segoe UI",Roboto,Oxygen,Ubuntu,Cantarell,"Fira Sans","Droid Sans","Helvetica Neue",Helvetica,Arial,sans-serif !important; 5 | } 6 | 7 | -------------------------------------------------------------------------------- /src/reducers/debug.js: -------------------------------------------------------------------------------- 1 | let debug = false; 2 | if (localStorage.debug) { 3 | debug = true; 4 | } 5 | const debugReducer = (state, action) => { 6 | if (debug) { 7 | console.log('action', action); 8 | } 9 | return true; 10 | } 11 | export default debugReducer; 12 | -------------------------------------------------------------------------------- /_templates/action/new/action.ejs.t: -------------------------------------------------------------------------------- 1 | --- 2 | to: src/actions/<%= name %>.js 3 | --- 4 | import { NEW_ACTION, ANOTHER_ACTION } from "../constants/<%= name %>"; 5 | 6 | export const newAction = (param1) => ({ type: NEW_ACTION, param1 }); 7 | export const anotherAction = () => ({ type: ANOTHER_ACTION }); 8 | -------------------------------------------------------------------------------- /src/views/Router/Router.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Route } from 'react-router'; 3 | import App from '../App'; 4 | import './index.css'; 5 | 6 | const mainRoutes = () => ( 7 | <> 8 | 9 | 10 | ); 11 | export default mainRoutes; -------------------------------------------------------------------------------- /docker-compose-example/docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.5" 2 | services: 3 | niolesk: 4 | image: "ghcr.io/webgiss/niolesk" 5 | ports: 6 | - "8017:80" 7 | hostname: "niolesk" 8 | restart: "always" 9 | environment: 10 | - "NIOLESK_KROKI_ENGINE=https://kroki.example.com/" 11 | -------------------------------------------------------------------------------- /src/views/Columns/Columns.css: -------------------------------------------------------------------------------- 1 | .xColumns { 2 | display: block; 3 | width: 100%; 4 | } 5 | .xColumns>* { 6 | display: inline-block; 7 | width: 50%; 8 | position: relative; 9 | } 10 | 11 | @media (max-width: 600px) { 12 | .xColumns>* { 13 | display: block; 14 | width: 100%; 15 | } 16 | } -------------------------------------------------------------------------------- /src/kroki/krokiInfo.js: -------------------------------------------------------------------------------- 1 | import exampleData from '../examples/data'; 2 | 3 | const krokiInfo = exampleData.filter(example => example.default).reduce((previous, current) => { 4 | previous[current.diagramType] = { name: current.title, example: current.example, language: current.language } 5 | return previous; 6 | }, {}); 7 | 8 | export default krokiInfo; -------------------------------------------------------------------------------- /.storybook/main.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | "core": { 3 | "builder": "webpack5" 4 | }, 5 | "stories": [ 6 | "../src/**/*.stories.mdx", 7 | "../src/**/*.stories.@(js|jsx|ts|tsx)" 8 | ], 9 | "addons": [ 10 | "@storybook/addon-links", 11 | "@storybook/addon-essentials", 12 | "@storybook/preset-create-react-app" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /src/actions/utils/delay.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Promise that delay execution 3 | * 4 | * @param {Number} timeout The timeout to delay 5 | * @param {T} data A data to pass to the promise 6 | * @return {Promise} 7 | * @template T 8 | */ 9 | const delay = (timeout, data) => new Promise((resolve, reject) => { 10 | setTimeout(() => resolve(data), timeout) 11 | }) 12 | export default delay; -------------------------------------------------------------------------------- /src/views/ExampleCard/ExampleCard.css: -------------------------------------------------------------------------------- 1 | .ExampleCard { 2 | } 3 | 4 | .ExampleCardDiagramOutter { 5 | position: relative; 6 | } 7 | .ExampleCardDiagramOutter::after { 8 | content: ""; 9 | display: block; 10 | padding-bottom: 100%; 11 | } 12 | 13 | .ExampleCardDiagram { 14 | position: absolute; 15 | width: 100%; 16 | height: 100%; 17 | object-fit: cover; 18 | } 19 | -------------------------------------------------------------------------------- /src/actions/__jest__/copy.js: -------------------------------------------------------------------------------- 1 | // ---------------------------------------- 2 | // Mock for copy 3 | let copy_mock = null; 4 | 5 | export const mockCopy = (copy_content) => copy_mock = copy_content; 6 | export const resetCopy = () => { copy_mock = null; } 7 | export const hasCopy = () => copy_mock !== null; 8 | export const getCopy = () => copy_mock; 9 | // ---------------------------------------- 10 | -------------------------------------------------------------------------------- /src/providers/debug.js: -------------------------------------------------------------------------------- 1 | let debug = false; 2 | if (localStorage.debug) { 3 | debug = true; 4 | } 5 | 6 | /** 7 | * @type {import('../init/reactRedux').Provider} 8 | * @template State 9 | */ 10 | const provider = { 11 | onNewState: (state) => { 12 | if (debug) { 13 | console.log('state', state); 14 | } 15 | } 16 | }; 17 | 18 | export default provider; 19 | -------------------------------------------------------------------------------- /src/constants/example.js: -------------------------------------------------------------------------------- 1 | export const OPEN_EXAMPLES = 'OPEN_EXAMPLES'; 2 | export const CHANGE_EXAMPLE_INDEX = 'CHANGE_EXAMPLE_INDEX'; 3 | export const IMPORT_EXAMPLE = 'IMPORT_EXAMPLE'; 4 | export const VIEW_EXAMPLE = 'VIEW_EXAMPLE'; 5 | export const CLOSE_EXAMPLE = 'CLOSE_EXAMPLE'; 6 | export const NEXT_EXAMPLE = 'NEXT_EXAMPLE'; 7 | export const PREV_EXAMPLE = 'PREV_EXAMPLE'; 8 | export const CHANGE_SEARCH = 'CHANGE_SEARCH'; -------------------------------------------------------------------------------- /src/views/WindowExampleCards/WindowExampleCards.css: -------------------------------------------------------------------------------- 1 | .WindowExampleCards { 2 | } 3 | 4 | .WindowExampleCardsSearch { 5 | font-size: 16px; 6 | width: 300px; 7 | } 8 | 9 | @media screen and (max-width: 500px) { 10 | .WindowExampleCardsSearch { 11 | width: 200px; 12 | } 13 | } 14 | 15 | @media screen and (max-width: 350px) { 16 | .WindowExampleCardsSearch { 17 | width: 120px; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /_templates/view/comp/component.ejs.t: -------------------------------------------------------------------------------- 1 | --- 2 | to: src/views/<%= name %>/<%= name %>.js 3 | --- 4 | import React from 'react'; 5 | import PropTypes from 'prop-types'; 6 | 7 | import './<%= name %>.css' 8 | 9 | const <%= name %> = ({ }) => { 10 | return
11 |
12 | } 13 | 14 | <%= name %>.propTypes = { 15 | }; 16 | 17 | <%= name %>.defaultProps = { 18 | }; 19 | 20 | export default <%= name %>; -------------------------------------------------------------------------------- /_templates/view/cont/component.ejs.t: -------------------------------------------------------------------------------- 1 | --- 2 | to: src/views/<%= name %>/<%= name %>.js 3 | --- 4 | import React from 'react'; 5 | import PropTypes from 'prop-types'; 6 | 7 | import './<%= name %>.css' 8 | 9 | const <%= name %> = ({ }) => { 10 | return
11 |
12 | } 13 | 14 | <%= name %>.propTypes = { 15 | }; 16 | 17 | <%= name %>.defaultProps = { 18 | }; 19 | 20 | export default <%= name %>; -------------------------------------------------------------------------------- /src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /src/views/Title/Title.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Title from './Title'; 3 | import { getComponenent } from '../storybook/stories' 4 | 5 | export default { 6 | title: 'Components/Title', 7 | component: Title, 8 | }; 9 | 10 | const Template = (args) => ; 11 | 12 | const defaultArgs = { 13 | } 14 | 15 | export const Default = getComponenent(Template, { ...defaultArgs }) 16 | -------------------------------------------------------------------------------- /src/views/CopyField/CopyField.css: -------------------------------------------------------------------------------- 1 | .CopyField { 2 | position: relative; 3 | } 4 | 5 | .CopyButton { 6 | position: absolute !important; 7 | top: 4px; 8 | right: 11px; 9 | } 10 | 11 | .CopyFieldPre { 12 | border-width: 2px !important; 13 | white-space: pre; 14 | overflow-x: scroll; 15 | background-color: #f8f8f8 !important; 16 | } 17 | 18 | .CopyField.copy-hover .CopyFieldPre { 19 | border: 2px solid #209cee; 20 | } 21 | -------------------------------------------------------------------------------- /src/views/SubTitle/SubTitle.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import SubTitle from './SubTitle'; 3 | import { getComponenent } from '../storybook/stories' 4 | 5 | export default { 6 | title: 'Components/SubTitle', 7 | component: SubTitle, 8 | }; 9 | 10 | const Template = (args) => <SubTitle {...args} />; 11 | 12 | const defaultArgs = { 13 | } 14 | 15 | export const Default = getComponenent(Template, { ...defaultArgs }) 16 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | 3 | packageExtensions: 4 | babel-preset-react-app@*: 5 | dependencies: 6 | "@babel/plugin-proposal-private-property-in-object": "*" 7 | 8 | plugins: 9 | - path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs 10 | spec: "@yarnpkg/plugin-interactive-tools" 11 | - path: .yarn/plugins/@yarnpkg/plugin-version.cjs 12 | spec: "@yarnpkg/plugin-version" 13 | 14 | yarnPath: .yarn/releases/yarn-3.1.1.cjs 15 | -------------------------------------------------------------------------------- /public/config.js: -------------------------------------------------------------------------------- 1 | window.config = { 2 | krokiEngineUrl: 'https://kroki.io/', 3 | analyticsContent: '', 4 | analyticsType: '', 5 | analyticsProviderName: '', 6 | analyticsProviderArg1: '', 7 | analyticsProviderArg2: '', 8 | analyticsProviderArg3: '', 9 | analyticsProviderArg4: '', 10 | analyticsProviderArg5: '', 11 | analyticsProviderArg6: '', 12 | analyticsProviderArg7: '', 13 | analyticsProviderArg8: '', 14 | analyticsProviderArg9: '', 15 | }; -------------------------------------------------------------------------------- /src/views/Window/Window.css: -------------------------------------------------------------------------------- 1 | .Window { 2 | position: fixed; 3 | left: 10px; 4 | right: 10px; 5 | } 6 | 7 | .WindowContent.window-centered-content > * { 8 | justify-content: center; 9 | } 10 | 11 | .WindowHeaderButtons { 12 | text-align: right; 13 | float: right; 14 | margin-top: -6px ; 15 | margin-right: -17.5px; 16 | } 17 | 18 | .WindowHeaderComponent { 19 | text-align: right; 20 | float: right; 21 | margin-top: -6px ; 22 | margin-right: -5px; 23 | } -------------------------------------------------------------------------------- /_templates/view/cont/index.ejs.t: -------------------------------------------------------------------------------- 1 | --- 2 | to: src/views/<%= name %>/index.js 3 | --- 4 | import actions from '../../actions' 5 | import { useDispatch, useSelector } from 'react-redux' 6 | import Internal from './<%= name %>' 7 | 8 | const <%= name %> = () => { 9 | const dispatch = useDispatch(); 10 | // const data = useSelector((state)=> state.reducerName.data) 11 | // const onEvent = (input) => dispatch(eventRaised(input)) 12 | return <Internal {...{ }} /> 13 | } 14 | 15 | export default <%= name %>; -------------------------------------------------------------------------------- /src/views/RenderUrl/RenderUrl.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import RenderUrl from './RenderUrl'; 3 | import { getComponenent } from '../storybook/stories' 4 | 5 | export default { 6 | title: 'Components/RenderUrl', 7 | component: RenderUrl, 8 | }; 9 | 10 | const Template = (args) => <RenderUrl {...args} />; 11 | 12 | const defaultArgs = { 13 | renderUrl: 'https://kroki.example.com/diagType/data==' 14 | } 15 | 16 | export const Default = getComponenent(Template, { ...defaultArgs }) 17 | -------------------------------------------------------------------------------- /src/views/Columns/Columns.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Columns from './Columns'; 3 | import { getComponenent } from '../storybook/stories' 4 | 5 | export default { 6 | title: 'Components/Columns', 7 | component: Columns, 8 | }; 9 | 10 | const Template = (args) => <Columns {...args} />; 11 | 12 | const defaultArgs = { 13 | children: [<div><p>First panel</p></div>, <div><p>Other panel</p></div>] 14 | } 15 | 16 | export const Default = getComponenent(Template, { ...defaultArgs }) 17 | -------------------------------------------------------------------------------- /src/views/Columns/Columns.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Grid } from 'semantic-ui-react'; 3 | 4 | import './Columns.css' 5 | 6 | const Columns = ({ children }) => { 7 | return <Grid columns={2} stackable> 8 | <Grid.Row verticalAlign='top'> 9 | <Grid.Column> 10 | {children[0]} 11 | </Grid.Column> 12 | <Grid.Column textAlign='center'> 13 | {children[1]} 14 | </Grid.Column> 15 | </Grid.Row> 16 | </Grid> 17 | 18 | } 19 | 20 | export default Columns; 21 | -------------------------------------------------------------------------------- /_templates/view/comp/stories.ejs.t: -------------------------------------------------------------------------------- 1 | --- 2 | to: src/views/<%= name %>/<%= name %>.stories.js 3 | --- 4 | import React from 'react'; 5 | import <%= name %> from './<%= name %>'; 6 | import { getComponenent } from '../storybook/stories'; 7 | 8 | export default { 9 | title: 'Components/<%= name %>', 10 | component: <%= name %>, 11 | }; 12 | 13 | const Template = (args) => <<%= name %> {...args} />; 14 | 15 | const defaultArgs = { 16 | }; 17 | 18 | export const Default = getComponenent(Template, { ...defaultArgs }); 19 | -------------------------------------------------------------------------------- /_templates/view/cont/stories.ejs.t: -------------------------------------------------------------------------------- 1 | --- 2 | to: src/views/<%= name %>/<%= name %>.stories.js 3 | --- 4 | import React from 'react'; 5 | import <%= name %> from './<%= name %>'; 6 | import { getComponenent } from '../storybook/stories'; 7 | 8 | export default { 9 | title: 'Components/<%= name %>', 10 | component: <%= name %>, 11 | }; 12 | 13 | const Template = (args) => <<%= name %> {...args} />; 14 | 15 | const defaultArgs = { 16 | }; 17 | 18 | export const Default = getComponenent(Template, { ...defaultArgs }); 19 | -------------------------------------------------------------------------------- /src/views/RenderUrl/index.js: -------------------------------------------------------------------------------- 1 | import Internal from './RenderUrl' 2 | import { renderUrlChanged } from '../../actions/editor'; 3 | import { useDispatch, useSelector } from 'react-redux' 4 | 5 | const RenderUrl = () => { 6 | const dispatch = useDispatch(); 7 | const renderUrl = useSelector((state) => state.editor.renderUrl) 8 | const renderUrlChangedAction = (renderUrl) => dispatch(renderUrlChanged(renderUrl)) 9 | return <Internal {...{ renderUrl, renderUrlChanged: renderUrlChangedAction }}/> 10 | } 11 | 12 | export default RenderUrl; -------------------------------------------------------------------------------- /.gitpod.yml: -------------------------------------------------------------------------------- 1 | # This configuration file was automatically generated by Gitpod. 2 | # Please adjust to your needs (see https://www.gitpod.io/docs/introduction/learn-gitpod/gitpod-yaml) 3 | # and commit this file to your remote git repository to share the goodness with others. 4 | 5 | # Learn more from ready-to-use templates: https://www.gitpod.io/docs/introduction/getting-started/quickstart 6 | 7 | tasks: 8 | - init: yarn install && yarn run build && make 9 | command: yarn run start 10 | 11 | image: 12 | file: .gitpod.Dockerfile 13 | 14 | -------------------------------------------------------------------------------- /src/examples/usecache.js: -------------------------------------------------------------------------------- 1 | import CryptoJS from 'crypto-js'; 2 | 3 | const md5 = (s) => CryptoJS.MD5(s).toString(); 4 | const cache = window.cache ? window.cache : []; 5 | 6 | export const getExampleUrl = (exampleItem) => { 7 | const ext = 'svg' 8 | const radical = [exampleItem.diagramType, ext, exampleItem.example].join('/') 9 | const sum = md5(radical) 10 | const filename = `${sum}.${ext}` 11 | 12 | if (cache.indexOf(filename) !== -1) { 13 | return `./cache/${filename}` 14 | } 15 | return null; 16 | } 17 | 18 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import Router from './views/Router'; 2 | import reportWebVitals from './reportWebVitals'; 3 | import { initReactRedux } from './init/reactRedux'; 4 | import reducers from './reducers'; 5 | import exportToWindow from './providers/exportToWindow'; 6 | import diagramChange from './providers/diagramChange'; 7 | import debug from './providers/debug'; 8 | import './debug' 9 | 10 | const providers = [ 11 | exportToWindow, 12 | diagramChange, 13 | debug, 14 | ]; 15 | initReactRedux(providers, '/', document.getElementById('root'), Router, reducers) 16 | 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "niolesk", 3 | "name": "Niolesk", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /src/reducers/index.js: -------------------------------------------------------------------------------- 1 | import { connectRouter } from 'connected-react-router' 2 | import { combineReducers } from 'redux'; 3 | import editor from './editor'; 4 | import example from './example'; 5 | import debug from './debug'; 6 | import niolesk from './niolesk'; 7 | 8 | /** 9 | * @param {import("../init/redux").BrowserHistory} history 10 | * @return {import("../init/redux").Reducer<State>} 11 | * @template State 12 | */ 13 | const createReducer = (history) => combineReducers({ 14 | router: connectRouter(history), 15 | editor, 16 | example, 17 | niolesk, 18 | debug, 19 | }); 20 | export default createReducer; -------------------------------------------------------------------------------- /src/views/DiagramType/index.js: -------------------------------------------------------------------------------- 1 | import { useDispatch, useSelector } from 'react-redux' 2 | import { diagramTypeChanged } from '../../actions/editor'; 3 | import Internal from './DiagramType' 4 | 5 | const DiagramType = () => { 6 | const dispatch = useDispatch(); 7 | const diagramTypes = useSelector((state)=> state.editor.diagramTypes) 8 | const diagramType = useSelector((state) => state.editor.diagramType) 9 | const onDiagramTypeChanged = (diagramType) => dispatch(diagramTypeChanged(diagramType)) 10 | return <Internal {...{ diagramType, diagramTypes, onDiagramTypeChanged }} /> 11 | } 12 | 13 | export default DiagramType; -------------------------------------------------------------------------------- /src/views/Editor/index.js: -------------------------------------------------------------------------------- 1 | import { diagramChanged } from '../../actions/editor'; 2 | import Internal from './Editor' 3 | import { useDispatch, useSelector } from 'react-redux' 4 | 5 | const Editor = () => { 6 | const dispatch = useDispatch(); 7 | const text = useSelector((state) => state.editor.diagramText); 8 | const height = useSelector((state) => state.editor.editorHeight); 9 | const language = useSelector((state) => state.editor.language); 10 | const onTextChanged = (text) => dispatch(diagramChanged(text)) 11 | return <Internal {...{ text, language, onTextChanged, height }} /> 12 | } 13 | 14 | export default Editor; -------------------------------------------------------------------------------- /src/views/RenderUrl/RenderUrl.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Input } from 'semantic-ui-react' 3 | 4 | const RenderUrl = ({ renderUrl, renderUrlChanged }) => { 5 | let changeHandler = null; 6 | if (renderUrlChanged) { 7 | changeHandler = (event) => renderUrlChanged(event.target.value); 8 | } 9 | 10 | return <Input 11 | fluid 12 | value={renderUrl} 13 | onChange={changeHandler} 14 | icon='cogs' 15 | iconPosition='left' 16 | placeholder='Render URL engine...' 17 | aria-label='Render URL engine' 18 | /> 19 | } 20 | 21 | export default RenderUrl; 22 | -------------------------------------------------------------------------------- /src/views/ShrinkableButton/ShrinkableButton.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ShrinkableButton from './ShrinkableButton'; 3 | import { getComponenent } from '../storybook/stories'; 4 | 5 | export default { 6 | title: 'Components/ShrinkableButton', 7 | component: ShrinkableButton, 8 | }; 9 | 10 | const Template = (args) => <ShrinkableButton {...args} />; 11 | 12 | const defaultArgs = { 13 | floated: 'right', 14 | icon: 'fire extinguisher', 15 | text: 'Fire extinguisher', 16 | textAlt: 'Fire', 17 | }; 18 | 19 | export const Default = getComponenent(Template, { ...defaultArgs }); 20 | -------------------------------------------------------------------------------- /.github/workflows/version.yml: -------------------------------------------------------------------------------- 1 | name: Create a new version/release 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | new_version: 8 | name: Create a new version 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/checkout@v2 12 | with: 13 | token: ${{ secrets.TOKEN_FOR_GIT }} 14 | 15 | - name: Create a new version 16 | shell: bash 17 | run: | 18 | git config --global user.name "Gissehel" 19 | git config --global user.email "gissehel@users.noreply.github.com" 20 | make release 21 | -------------------------------------------------------------------------------- /src/views/WindowExampleCards/index.js: -------------------------------------------------------------------------------- 1 | import { useDispatch, useSelector } from 'react-redux' 2 | import Internal from './WindowExampleCards' 3 | import { closeExample, updateSearch } from '../../actions/example'; 4 | 5 | const WindowExampleCards = () => { 6 | const dispatch = useDispatch(); 7 | const onClose = () => dispatch(closeExample()); 8 | const open = useSelector((state) => state.example.windowExampleCardsOpened) 9 | const search = useSelector((state) => state.example.search) 10 | const onSearchChange = (search) => dispatch(updateSearch(search)) 11 | return <Internal {...{ open, onClose, search, onSearchChange }} /> 12 | } 13 | 14 | export default WindowExampleCards; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # Yarn pnp 9 | .pnp.* 10 | .yarn/* 11 | !.yarn/patches 12 | !.yarn/plugins 13 | !.yarn/releases 14 | !.yarn/sdks 15 | 16 | # testing 17 | /coverage 18 | 19 | # production 20 | /build 21 | 22 | # misc 23 | .DS_Store 24 | .env.local 25 | .env.development.local 26 | .env.test.local 27 | .env.production.local 28 | 29 | npm-debug.log* 30 | yarn-debug.log* 31 | yarn-error.log* 32 | 33 | # vs code 34 | .vscode/*.log 35 | .vscode/ 36 | 37 | /storybook-static 38 | /public/cache.js 39 | /public/cache/* 40 | 41 | jest_html_reporters.html 42 | -------------------------------------------------------------------------------- /src/views/SubTitle/SubTitle.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | import GitInfo from 'react-git-info/macro'; 4 | 5 | import './SubTitle.css' 6 | 7 | const gitInfo = GitInfo(); 8 | const version = `${process.env.REACT_APP_VERSION}-${gitInfo.commit.hash.substr(0, 8)}`; 9 | 10 | const SubTitle = () => { 11 | return <div className='SubTitle' basic="true"> 12 | <div>Edit <b>diagrams</b> from <b>textual</b> descriptions! : A <a href='https://kroki.io'>kroki</a> interface.</div> 13 | <div className='SubTitleSmall'>Github project page: <a href='https://github.com/webgiss/niolesk/'>https://github.com/webgiss/niolesk/</a> - Version {version}</div> 14 | </div> 15 | } 16 | 17 | export default SubTitle; 18 | -------------------------------------------------------------------------------- /src/views/WindowImportUrl/index.js: -------------------------------------------------------------------------------- 1 | import { useDispatch, useSelector } from 'react-redux' 2 | import Internal from './WindowImportUrl' 3 | import { importUrl, closeImportUrl, updateUrl } from '../../actions/editor'; 4 | 5 | const WindowImportUrl = () => { 6 | const dispatch = useDispatch(); 7 | 8 | const onClose = () => dispatch(closeImportUrl()); 9 | const open = useSelector((state) => state.editor.windowImportUrlOpened) 10 | const url = useSelector((state) => state.editor.windowImportUrl) 11 | const onImportUrl = (url) => dispatch(importUrl(url)); 12 | const onUrlChange = (url) => dispatch(updateUrl(url)); 13 | 14 | return <Internal {...{ open, onClose, onImportUrl, onUrlChange, url }} /> 15 | } 16 | 17 | export default WindowImportUrl; -------------------------------------------------------------------------------- /src/views/ExampleCards/ExampleCards.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import './ExampleCards.css' 5 | import { Card } from 'semantic-ui-react'; 6 | import ExampleCard from '../ExampleCard/ExampleCard'; 7 | 8 | const ExampleCards = ({ cards }) => { 9 | return <Card.Group> 10 | {cards.map((card,index) => <ExampleCard {...card} key={index} />)} 11 | </Card.Group> 12 | } 13 | 14 | ExampleCards.propTypes = { 15 | cards: PropTypes.arrayOf(PropTypes.shape({ 16 | diagType: PropTypes.string.isRequired, 17 | description: PropTypes.string.isRequired, 18 | diagUrl: PropTypes.string.isRequired, 19 | })).isRequired, 20 | }; 21 | 22 | ExampleCards.defaultProps = { 23 | }; 24 | 25 | export default ExampleCards; 26 | -------------------------------------------------------------------------------- /src/views/Render/Render.css: -------------------------------------------------------------------------------- 1 | .Render { 2 | background-color: none; 3 | text-align: center; 4 | overflow: hidden; 5 | } 6 | 7 | .zenMode .Render { 8 | overflow: auto; 9 | position: relative; 10 | } 11 | 12 | .RenderImage { 13 | width: auto; 14 | height: auto; 15 | position: relative; 16 | } 17 | 18 | .zenMode .RenderDiagramZone { 19 | position: relative; 20 | } 21 | 22 | .zenMode .RenderImage { 23 | width: auto; 24 | position: relative; 25 | } 26 | 27 | 28 | .zenMode .RenderEditMessage { 29 | height: 25px; 30 | } 31 | 32 | div.react-transform-wrapper { 33 | -webkit-user-select: all; 34 | -khtml-user-select: all; 35 | -moz-user-select: all; 36 | -ms-user-select: all; 37 | user-select: all; 38 | } 39 | 40 | .RenderImageError { 41 | 42 | } -------------------------------------------------------------------------------- /src/actions/example.js: -------------------------------------------------------------------------------- 1 | import { CHANGE_EXAMPLE_INDEX, OPEN_EXAMPLES, IMPORT_EXAMPLE, VIEW_EXAMPLE, CLOSE_EXAMPLE, PREV_EXAMPLE, NEXT_EXAMPLE, CHANGE_SEARCH } from "../constants/example"; 2 | 3 | export const openExamples = () => ({ type: OPEN_EXAMPLES }); 4 | export const changeExampleIndex = (exampleIndex) => ({ type: CHANGE_EXAMPLE_INDEX, exampleIndex }) 5 | export const importExample = (diagramText, diagramType) => ({ type: IMPORT_EXAMPLE, diagramText, diagramType }) 6 | export const viewExample = (exampleIndex) => ({ type: VIEW_EXAMPLE, exampleIndex }) 7 | export const closeExample = () => ({ type: CLOSE_EXAMPLE }) 8 | export const prevExample = () => ({ type: PREV_EXAMPLE }) 9 | export const nextExample = () => ({ type: NEXT_EXAMPLE }) 10 | export const updateSearch = (search) => ({ type: CHANGE_SEARCH, search }) -------------------------------------------------------------------------------- /src/kroki/coder.js: -------------------------------------------------------------------------------- 1 | import pako from 'pako'; 2 | 3 | 4 | let util = {} 5 | 6 | try { 7 | util = require('util') 8 | } catch { 9 | } 10 | 11 | let TextEncoder = null; 12 | 13 | TextEncoder = window.TextEncoder || util.TextEncoder; 14 | 15 | export const encode = (source) => { 16 | const data = new TextEncoder('utf-8').encode(source); 17 | const compressed = [...pako.deflate(data, { level: 9 })].map((x) => String.fromCharCode(x)).join(''); 18 | return btoa(compressed).replace(/\+/g, '-').replace(/\//g, '_'); 19 | } 20 | export const decode = (coded) => { 21 | const compressed = atob(coded.replace(/-/g, '+').replace(/_/g, '/')); 22 | return pako.inflate(compressed.split('').map(x => x.charCodeAt(0)), { to: 'string' }); 23 | } 24 | 25 | window.coder = { encode, decode }; 26 | window.pako = pako; 27 | -------------------------------------------------------------------------------- /src/views/ShrinkableButton/ShrinkableButton.css: -------------------------------------------------------------------------------- 1 | .ShrinkableTextAlt { 2 | display: none; 3 | } 4 | 5 | @media screen and (max-width: 700px) { 6 | .ShrinkableText { 7 | display: none; 8 | } 9 | .ShrinkableTextAlt { 10 | display: inline; 11 | } 12 | } 13 | 14 | @media screen and (max-width: 600px) { 15 | .ShrinkableButton { 16 | width: 4em; 17 | white-space: pre; 18 | overflow: hidden; 19 | } 20 | 21 | .ShrinkableText { 22 | display: none; 23 | } 24 | 25 | .ShrinkableTextAlt { 26 | display: none; 27 | } 28 | } 29 | 30 | @media screen and (max-width: 420px) { 31 | .ShrinkableButton { 32 | width: 2em; 33 | } 34 | .ShrinkableButton i.icon { 35 | font-size: 0.75em; 36 | position: relative; 37 | left: -4px; 38 | top: -2px; 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/views/Editor/Editor.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Editor from './Editor'; 3 | import { getComponenent } from '../storybook/stories' 4 | 5 | export default { 6 | title: 'Components/Editor', 7 | component: Editor, 8 | }; 9 | 10 | const Template = (args) => <Editor {...args} />; 11 | 12 | const defaultArgs = { 13 | text: 'this is\na\ntext\n with\n indentation\n.', 14 | height: 700, 15 | language: undefined, 16 | } 17 | 18 | const jsonText = '{"number":7,"text":"text","array":[7,8,9],"subobj":{"item":12,"data":"poide"},"nothing":null}' 19 | 20 | export const Default = getComponenent(Template, { ...defaultArgs }) 21 | export const WithOtherText = getComponenent(Template, { ...defaultArgs, text: 'def f(x):\n return x+5\n\n' }) 22 | export const WithJson = getComponenent(Template, { ...defaultArgs, text: jsonText, language: 'json' }) 23 | -------------------------------------------------------------------------------- /src/views/ExampleCards/ExampleCards.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ExampleCards from './ExampleCards'; 3 | import { getComponenent } from '../storybook/stories'; 4 | import exampleData from '../../examples/data'; 5 | import { createKrokiUrl } from '../../kroki/utils'; 6 | 7 | export default { 8 | title: 'Components/ExampleCards', 9 | component: ExampleCards, 10 | }; 11 | 12 | const Template = (args) => <ExampleCards {...args} />; 13 | 14 | const defaultArgs = { 15 | cards:exampleData.map((example)=>({ 16 | diagType: example.title, 17 | description: example.description, 18 | diagUrl: createKrokiUrl('https://kroki.io/', example.diagramType, 'svg', example.example), 19 | onView: () => {}, 20 | onImport: () => {}, 21 | })), 22 | }; 23 | 24 | export const Default = getComponenent(Template, { ...defaultArgs }); 25 | -------------------------------------------------------------------------------- /src/init/react.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import { Provider } from 'react-redux'; 4 | import { ConnectedRouter } from 'connected-react-router' 5 | 6 | /** 7 | * @typedef {import('history').BrowserHistory} BrowserHistory 8 | */ 9 | 10 | /** 11 | * Init react layer of the application 12 | * 13 | * @param {import('./redux').Store<State>} store 14 | * @param {BrowserHistory} history 15 | * @param {()=>JSX.Element} ReactNode 16 | * @param {HTMLElement} domNode 17 | * @template State 18 | */ 19 | export const initReact = (store, history, domNode, ReactNode) => { 20 | ReactDOM.render( 21 | <React.StrictMode> 22 | <Provider store={store}> 23 | <ConnectedRouter history={history}> 24 | <ReactNode location={{}}/> 25 | </ConnectedRouter> 26 | </Provider>, 27 | </React.StrictMode>, 28 | domNode 29 | ); 30 | } 31 | 32 | -------------------------------------------------------------------------------- /src/constants/editor.js: -------------------------------------------------------------------------------- 1 | export const COPY_TEXT = 'COPY_TEXT'; 2 | export const TEXT_COPIED = 'TEXT_COPIED'; 3 | export const COPY_BUTTON_HOVERED = 'COPY_BUTTON_HOVERED'; 4 | export const EDITOR_CHANGED = 'EDITOR_CHANGED'; 5 | export const RENDERURL_CHANGED = 'RENDERURL_CHANGED'; 6 | export const DIAGRAM_CHANGED = 'DIAGRAM_CHANGED'; 7 | export const DIAGRAM_CHANGED_UPDATE = 'DIAGRAM_CHANGED_UPDATE'; 8 | export const DIAGRAM_TYPE_CHANGED = 'DIAGRAM_TYPE_CHANGED'; 9 | export const OPEN_IMPORT_URL = 'OPEN_IMPORT_URL'; 10 | export const IMPORT_URL = 'IMPORT_URL'; 11 | export const CLOSE_IMPORT_URL = 'CLOSE_IMPORT_URL'; 12 | export const UPDATE_IMPORT_URL = 'UPDATE_IMPORT_URL'; 13 | export const DIAGRAM_HAS_ERROR = 'DIAGRAM_HAS_ERROR'; 14 | export const ZEN_MODE_CHANGED = 'ZEN_MODE_CHANGED'; 15 | export const KEY_PRESSED = 'KEY_PRESSED'; 16 | export const WINDOW_RESIZED = 'WINDOW_RESIZED'; 17 | export const RENDER_EDIT_SIZE_CHANGED = 'RENDER_WIDTH_CHANGED'; 18 | -------------------------------------------------------------------------------- /src/views/Editor/Editor.css: -------------------------------------------------------------------------------- 1 | .Editor { 2 | box-sizing: border-box; 3 | padding: 0; 4 | vertical-align: top; 5 | } 6 | 7 | .MonacoEditor { 8 | border: 1px solid; 9 | border-color: #dbdbdb; 10 | } 11 | .EditorTextArea { 12 | font-family: Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace; 13 | box-sizing: border-box; 14 | margin: 0; 15 | height: 700px; 16 | overflow-x: scroll; 17 | overflow-y: auto; 18 | white-space: pre; 19 | 20 | display: block; 21 | padding: calc(.75em - 1px); 22 | resize: vertical; 23 | 24 | width: 100%; 25 | 26 | line-height: 1.5; 27 | 28 | border: 1px solid; 29 | background-color: #fff; 30 | border-color: #dbdbdb; 31 | color: #363636; 32 | border-radius: 5px; 33 | 34 | -moz-appearance: none; 35 | -webkit-appearance: none; 36 | align-items: center; 37 | 38 | font-size: 1rem; 39 | justify-content: flex-start; 40 | } 41 | -------------------------------------------------------------------------------- /src/reducers/utils/analytics_functions.js: -------------------------------------------------------------------------------- 1 | const get_analytic_content = (provided_content, provided_type, provider_name, provider_data, providers) => { 2 | { 3 | let content = provided_content 4 | let type = provided_type 5 | if (content && content !== '') { 6 | if (!type || type === '') { 7 | type = 'html' 8 | } 9 | return [{ content, type }] 10 | } 11 | } 12 | if (providers[provider_name] === undefined) { 13 | return null 14 | } 15 | let result = [] 16 | for (let provider_part of providers[provider_name]) { 17 | let { content, type } = provider_part 18 | for (let index = 1; index <= provider_data.length; index++) { 19 | content = content.replaceAll(`{${index}}`, provider_data[index - 1]) 20 | } 21 | result.push({ content, type }) 22 | } 23 | return result 24 | } 25 | 26 | export default get_analytic_content 27 | -------------------------------------------------------------------------------- /src/views/App/index.js: -------------------------------------------------------------------------------- 1 | import { openExamples } from '../../actions/example' 2 | import { openImportUrl, keyPressed, onWindowResized, changeZenMode } from '../../actions/editor' 3 | import { useDispatch, useSelector } from 'react-redux' 4 | import Internal from './App' 5 | 6 | const App = () => { 7 | const zenMode = useSelector((state) => state.editor.zenMode) 8 | const dispatch = useDispatch(); 9 | const onExamples = () => dispatch(openExamples()); 10 | const onImportUrl = () => dispatch(openImportUrl()); 11 | const onKey = (keyInfo) => dispatch(keyPressed(keyInfo)); 12 | const onResize = (width, height) => dispatch(onWindowResized(width, height)); 13 | const onSetZenMode = () => dispatch(changeZenMode(true)); 14 | const analytics = useSelector((state) => state.niolesk.analytics) 15 | 16 | window.dispatch = dispatch 17 | 18 | return Internal({ onExamples, onImportUrl, zenMode, onKey, onResize, onSetZenMode, analytics }); 19 | 20 | } 21 | 22 | export default App; 23 | -------------------------------------------------------------------------------- /src/providers/diagramChange.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @typedef {import('../init/reactRedux').StateChange<State, T>} StateChange 3 | * @template State 4 | * @template T 5 | */ 6 | 7 | /** 8 | * @type {StateChange<State,string>} 9 | * @template State 10 | */ 11 | const onNewDiagram = { 12 | name: 'onNewDiagram', 13 | getStateValue: (state) => { 14 | if (state.editor) { 15 | return state.editor.diagramUrl; 16 | } 17 | return null; 18 | }, 19 | onNewValue: (currentValue, previousValue, currentState, subscribeParams) => { 20 | const {history} = subscribeParams; 21 | if (currentValue) { 22 | history.push(`#${currentValue}`); 23 | } 24 | }, 25 | } 26 | 27 | /** 28 | * @type {import('../init/reactRedux').Provider<State>} 29 | * @template State 30 | */ 31 | const provider = { 32 | stateChangeManager: [onNewDiagram], 33 | onNewState: (state) => { 34 | window.state = state; 35 | } 36 | }; 37 | 38 | export default provider; 39 | -------------------------------------------------------------------------------- /src/views/ShrinkableButton/ShrinkableButton.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import './ShrinkableButton.css' 5 | import classNames from 'classnames'; 6 | import { Button, Icon } from 'semantic-ui-react'; 7 | 8 | const ShrinkableButton = ({ floated, icon, text, textAlt, onClick }) => { 9 | return <Button className={classNames('ShrinkableButton')} floated={floated} onClick={onClick}> 10 | <Icon name={icon} /> 11 | <span className='ShrinkableText'>{text}</span> 12 | <span className='ShrinkableTextAlt'>{textAlt}</span> 13 | </Button> 14 | } 15 | 16 | ShrinkableButton.propTypes = { 17 | floated: PropTypes.string.isRequired, 18 | icon: PropTypes.string.isRequired, 19 | text: PropTypes.string.isRequired, 20 | textAlt: PropTypes.string.isRequired, 21 | onClick: PropTypes.func.isRequired, 22 | }; 23 | 24 | ShrinkableButton.defaultProps = { 25 | floated: undefined, 26 | icon: '', 27 | text: '', 28 | textAlt: '', 29 | }; 30 | 31 | export default ShrinkableButton; -------------------------------------------------------------------------------- /src/views/WindowExampleDetail/index.js: -------------------------------------------------------------------------------- 1 | import { useDispatch, useSelector } from 'react-redux' 2 | import Internal from './WindowExampleDetail' 3 | import { closeExample, importExample, prevExample, nextExample } from '../../actions/example'; 4 | import { decode } from '../../kroki/coder'; 5 | 6 | const WindowExampleDetail = () => { 7 | const dispatch = useDispatch(); 8 | const examples = useSelector((state)=> state.example.examples) 9 | const exampleIndex = useSelector((state)=> state.example.exampleIndex) 10 | 11 | const onClose = () => dispatch(closeExample()); 12 | const onImport = () => dispatch(importExample(decode(examples[exampleIndex].example), examples[exampleIndex].diagramType)); 13 | const open = useSelector((state) => state.example.windowExampleDetailsOpened) 14 | const onPrevExample = () => dispatch(prevExample()); 15 | const onNextExample = () => dispatch(nextExample()); 16 | return <Internal {...{ open, onClose, onImport, onPrevExample, onNextExample }} /> 17 | } 18 | 19 | export default WindowExampleDetail; -------------------------------------------------------------------------------- /src/views/ExampleCards/index.js: -------------------------------------------------------------------------------- 1 | import { useDispatch, useSelector } from 'react-redux' 2 | import { createKrokiUrl } from '../../kroki/utils'; 3 | import Internal from './ExampleCards' 4 | import {viewExample,importExample } from '../../actions/example' 5 | import { decode } from '../../kroki/coder'; 6 | 7 | const ExampleCards = () => { 8 | const dispatch = useDispatch(); 9 | const examples = useSelector((state) => state.example.filteredExamples) 10 | const renderUrl = useSelector((state) => state.editor.renderUrl) 11 | 12 | console.log({examples}) 13 | const cards = examples.map((example)=>({ 14 | diagType: example.title, 15 | description: example.description, 16 | diagUrl: example.url || createKrokiUrl(renderUrl, example.diagramType, 'svg', example.example), 17 | onView: () => dispatch(viewExample(example.id)), 18 | onImport: () => dispatch(importExample(decode(example.example), example.diagramType)), 19 | })) 20 | 21 | return <Internal {...{ cards }} /> 22 | } 23 | 24 | export default ExampleCards; -------------------------------------------------------------------------------- /src/views/CopyZone/CopyZone.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Divider, Form, Segment } from 'semantic-ui-react' 3 | import CopyField from '../CopyField' 4 | import './CopyZone.css' 5 | 6 | const CopyZone = () => { 7 | return <Segment className='CopyZone' basic> 8 | <Divider/> 9 | <Form> 10 | <Form.Field> 11 | <label>Image url</label> 12 | <CopyField scope='image' /> 13 | </Form.Field> 14 | <Form.Field> 15 | <label>Edit url</label> 16 | <CopyField scope='edit' /> 17 | </Form.Field> 18 | <Form.Field> 19 | <label>Markdown content</label> 20 | <CopyField scope='markdown' /> 21 | </Form.Field> 22 | <Form.Field> 23 | <label>Markdown content with source as comment</label> 24 | <CopyField scope='markdownsource' /> 25 | </Form.Field> 26 | </Form> 27 | </Segment> 28 | } 29 | 30 | CopyZone.propTypes = { 31 | } 32 | 33 | export default CopyZone; 34 | -------------------------------------------------------------------------------- /src/reducers/niolesk.js: -------------------------------------------------------------------------------- 1 | import get_analytic_content from "./utils/analytics_functions" 2 | import { createReducer } from "./utils/createReducer" 3 | 4 | const local_config = window.config || {} 5 | const local_config_analytic_providers = window.config_analytic_providers || {} 6 | 7 | export const initialState = { 8 | analytics: get_analytic_content( 9 | local_config.analyticsContent, 10 | local_config.analyticsType, 11 | local_config.analyticsProviderName, 12 | [ 13 | local_config.analyticsProviderArg1, 14 | local_config.analyticsProviderArg2, 15 | local_config.analyticsProviderArg3, 16 | local_config.analyticsProviderArg4, 17 | local_config.analyticsProviderArg5, 18 | local_config.analyticsProviderArg6, 19 | local_config.analyticsProviderArg7, 20 | local_config.analyticsProviderArg8, 21 | local_config.analyticsProviderArg9, 22 | ], 23 | local_config_analytic_providers 24 | ), 25 | } 26 | 27 | export default createReducer({ 28 | }, initialState) 29 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 gissehel 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /res/niolesk.svg: -------------------------------------------------------------------------------- 1 | <?xml version="1.0" encoding="utf-8"?> 2 | <svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="64" height="64" 3 | xmlns:xlink="http://www.w3.org/1999/xlink" 4 | > 5 | <title>Niolesk 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /src/views/storybook/store.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Provider } from 'react-redux'; 3 | 4 | export const getReduxMockDecorator = () => { 5 | let state = {}; 6 | const listeners = []; 7 | const subscribe = (listener) => { 8 | const unsubscribe = () => { 9 | const position = listeners.indexOf(listener); 10 | if (position > -1) { 11 | listeners.splice(position, 1); 12 | } 13 | }; 14 | if (listeners.indexOf(listener) === -1) { 15 | listeners.push(listener); 16 | } 17 | return unsubscribe; 18 | } 19 | const store = { 20 | dispatch: () => { }, 21 | getState: () => state, 22 | subscribe, 23 | } 24 | 25 | const setState = (newState) => { 26 | state = newState; 27 | listeners.forEach((listener) => listener()); 28 | }; 29 | 30 | return { 31 | setState, 32 | decorator: (getStory) => ( 33 | 34 | {getStory()} 35 | 36 | ) 37 | }; 38 | }; 39 | -------------------------------------------------------------------------------- /src/views/App/App.css: -------------------------------------------------------------------------------- 1 | 2 | .App { 3 | max-width: 1200px; 4 | margin-top: 10px; 5 | margin-bottom: 0px; 6 | margin-left: auto; 7 | margin-right: auto; 8 | } 9 | 10 | .code { 11 | font-family: source-code-pro, Menlo, Monaco, 'Fira Code', Consolas, 'Courier New', monospace !important; 12 | } 13 | 14 | .diagramParams { 15 | margin-bottom: 1rem; 16 | } 17 | 18 | #root { 19 | overflow: hidden; 20 | } 21 | 22 | .zenMode .MainPanel { 23 | position: fixed; 24 | left: 0px; 25 | top: 0px; 26 | bottom: 0px; 27 | right: 0px; 28 | } 29 | 30 | .zenMode .NonZen { 31 | display: none; 32 | } 33 | 34 | .controlZone { 35 | display: flex; 36 | flex-direction: row; 37 | flex-wrap: nowrap; 38 | align-content: center; 39 | } 40 | 41 | .diagramTypeZone { 42 | flex-grow: 0; 43 | flex-basis: auto; 44 | min-width: max-content; 45 | } 46 | 47 | .buttonsZone { 48 | flex-grow: 1; 49 | flex-basis: auto; 50 | display: flex; 51 | flex-direction: row; 52 | justify-content: flex-end; 53 | flex-wrap: wrap; 54 | } 55 | 56 | .analyticsPanel { 57 | line-height: 1px; 58 | height: 1px; 59 | } -------------------------------------------------------------------------------- /src/views/WindowExampleDetail/WindowExampleDetail.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import WindowExampleDetail from './index'; 3 | import { getComponenent } from '../storybook/stories'; 4 | import { getReduxMockDecorator } from '../storybook/store'; 5 | import exampleData from "../../examples/data"; 6 | 7 | export default { 8 | title: 'Components/WindowExampleDetail', 9 | component: WindowExampleDetail, 10 | }; 11 | 12 | const defaultState = { 13 | editor: { 14 | renderUrl : 'https://kroki.io/' 15 | }, 16 | example: { 17 | windowExampleCardsOpened: false, 18 | windowExampleDetailsOpened: true, 19 | exampleIndex: 2, 20 | examples: exampleData, 21 | filteredExamples: exampleData, 22 | search: '', 23 | } 24 | }; 25 | 26 | const Template = (args) => { 27 | const { setState, decorator } = getReduxMockDecorator() 28 | let state = defaultState 29 | 30 | setState(state) 31 | return decorator(() => ) 32 | }; 33 | 34 | const defaultArgs = { 35 | }; 36 | 37 | export const Default = getComponenent(Template, { ...defaultArgs }); 38 | -------------------------------------------------------------------------------- /src/views/WindowExampleCards/WindowExampleCards.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import WindowExampleCards from './index'; 3 | import { getReduxMockDecorator } from '../storybook/store'; 4 | import { getComponenent } from '../storybook/stories'; 5 | import exampleData from "../../examples/data"; 6 | 7 | export default { 8 | title: 'Components/WindowExampleCards', 9 | component: WindowExampleCards, 10 | }; 11 | 12 | const defaultState = { 13 | editor: { 14 | renderUrl : 'https://kroki.io/' 15 | }, 16 | example: { 17 | windowExampleCardsOpened: true, 18 | windowExampleDetailsOpened: false, 19 | exampleIndex: 2, 20 | examples: exampleData, 21 | filteredExamples: exampleData, 22 | search: '', 23 | } 24 | }; 25 | 26 | const Template = (args) => { 27 | const { setState, decorator } = getReduxMockDecorator() 28 | let state = defaultState 29 | 30 | setState(state) 31 | return decorator(() => ) 32 | }; 33 | 34 | const defaultArgs = { 35 | open: true, 36 | }; 37 | 38 | export const Default = getComponenent(Template, { ...defaultArgs }); 39 | -------------------------------------------------------------------------------- /src/actions/__jest__/thunk.js: -------------------------------------------------------------------------------- 1 | import delay from '../utils/delay'; 2 | 3 | // ---------------------------------------- 4 | // Mock for dispatch 5 | const dispatchActions = []; 6 | 7 | export const resetDispatchActions = () => { dispatchActions.splice(0); } 8 | const mockDispatch = (action) => { 9 | dispatchActions.push(action); 10 | } 11 | export const getDispatchActions = () => dispatchActions; 12 | // ---------------------------------------- 13 | 14 | // ---------------------------------------- 15 | // Mock for get_state 16 | let mockState = {}; 17 | export const resetMockState = () => mockState = {}; 18 | export const setMockState = (state) => mockState = state; 19 | const mockGetState = () => mockState; 20 | // ---------------------------------------- 21 | 22 | // ---------------------------------------- 23 | // Mock for execution of an action that use thunk 24 | export const executeThunkAction = async (action) => { 25 | const result = action(); 26 | expect(typeof result).toBe('function') 27 | const resultPromise = result(mockDispatch, mockGetState); 28 | await delay(0); 29 | return resultPromise; 30 | } 31 | // ---------------------------------------- 32 | -------------------------------------------------------------------------------- /src/views/WindowExampleDetail/WindowExampleDetail.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Window from '../Window' 3 | import ExampleDetail from '../ExampleDetail' 4 | import PropTypes from 'prop-types'; 5 | 6 | import './WindowExampleDetail.css' 7 | import { Button } from 'semantic-ui-react'; 8 | 9 | const WindowExampleDetail = ({ open, onClose, onImport, onPrevExample, onNextExample }) => { 10 | return onImport()}>Import } 17 | headerButtons={<> 18 | } 18 | > 19 |
onImportUrl(url)}> 20 | open && element && element.focus()} 25 | fluid={true} 26 | /> 27 |
28 |
29 | } 30 | 31 | WindowImportUrl.propTypes = { 32 | open: PropTypes.bool.isRequired, 33 | onClose: PropTypes.func.isRequired, 34 | onImportUrl: PropTypes.func.isRequired, 35 | onUrlChange: PropTypes.func.isRequired, 36 | url: PropTypes.string.isRequired, 37 | }; 38 | 39 | WindowImportUrl.defaultProps = { 40 | }; 41 | 42 | export default WindowImportUrl; 43 | -------------------------------------------------------------------------------- /Dockerfile-for-local-build: -------------------------------------------------------------------------------- 1 | ARG NGINXIMAGE=nginx:alpine 2 | 3 | #---------------------------------------- 4 | 5 | FROM ${NGINXIMAGE} 6 | 7 | ARG VCS_REF=working-copy 8 | ARG BUILD_DATE=now 9 | ARG VERSION=dev 10 | 11 | LABEL \ 12 | org.opencontainers.image.created="${BUILD_DATE}" \ 13 | org.opencontainers.image.authors="gissehel" \ 14 | org.opencontainers.image.url="https://github.com/webgiss/niolesk" \ 15 | org.opencontainers.image.source="https://github.com/webgiss/niolesk" \ 16 | org.opencontainers.image.version="${VERSION}" \ 17 | org.opencontainers.image.revision="${VCS_REF}" \ 18 | org.opencontainers.image.vendor="gissehel" \ 19 | org.opencontainers.image.ref.name="ghcr.io/webgiss/niolesk" \ 20 | org.opencontainers.image.title="niolesk" \ 21 | org.opencontainers.image.description="Image for niolesk" \ 22 | org.label-schema.build-date="${BUILD_DATE}" \ 23 | org.label-schema.vcs-ref="${VCS_REF}" \ 24 | org.label-schema.name="niolesk" \ 25 | org.label-schema.version="${VERSION}" \ 26 | org.label-schema.vendor="gissehel" \ 27 | org.label-schema.vcs-url="https://github.com/webgiss/niolesk" \ 28 | org.label-schema.schema-version="1.0" \ 29 | maintainer="Gissehel " 30 | 31 | COPY docker-res/update-config.sh /docker-entrypoint.d/update-config.sh 32 | COPY build/ /usr/share/nginx/html/ 33 | COPY --chmod=0666 build/config.js /usr/share/nginx/html/ 34 | -------------------------------------------------------------------------------- /src/views/ExampleDetail/index.js: -------------------------------------------------------------------------------- 1 | import { changeExampleIndex } from '../../actions/example' 2 | import { useDispatch, useSelector } from 'react-redux' 3 | import { decode } from "../../kroki/coder"; 4 | import Internal from './ExampleDetail' 5 | import { createKrokiUrl } from '../../kroki/utils'; 6 | 7 | const ExampleDetail = () => { 8 | const dispatch = useDispatch(); 9 | const examples = useSelector((state) => state.example.examples) 10 | const items = examples.map((example) => [example.title, example.description]) 11 | const renderUrl = useSelector((state) => state.editor.renderUrl) 12 | 13 | let itemIndex = useSelector((state) => state.example.exampleIndex); 14 | // console.log('ExampleDetail', { itemIndex, examples }) 15 | if (itemIndex < 0 || itemIndex >= examples.length) { 16 | itemIndex = 0; 17 | } 18 | 19 | const diagramType = examples[itemIndex].diagramType; 20 | const description = examples[itemIndex].description; 21 | const codedDiagramTextText = examples[itemIndex].example; 22 | const doc = examples[itemIndex].doc; 23 | const diagramText = decode(codedDiagramTextText); 24 | const diagUrl = examples[itemIndex].url || createKrokiUrl(renderUrl, diagramType, 'svg', codedDiagramTextText); 25 | const onSelectItem = (index) => dispatch(changeExampleIndex(index)) 26 | 27 | return 28 | } 29 | 30 | export default ExampleDetail; -------------------------------------------------------------------------------- /src/views/ExampleCard/ExampleCard.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ExampleCard from './ExampleCard'; 3 | import { getComponenent } from '../storybook/stories'; 4 | 5 | export default { 6 | title: 'Components/ExampleCard', 7 | component: ExampleCard, 8 | }; 9 | 10 | const Template = (args) => ; 11 | 12 | const defaultArgs = { 13 | diagType: 'Diagram Type', 14 | description: 'Description', 15 | diagUrl: 'https://kroki.io/blockdiag/svg/eNpdzDEKQjEQhOHeU4zpPYFoYesRxGJ9bwghMSsbUYJ4d10UCZbDfPynolOek0Q8FsDeNCestoisNLmy-Qg7R3Blcm5hPcr0ITdaB6X15fv-_YdJixo2CNHI2lmK3sPRA__RwV5SzV80ZAegJjXSyfMFptc71w==', 16 | }; 17 | 18 | export const Default = getComponenent(Template, { ...defaultArgs }); 19 | export const WithOtherDiagram = getComponenent(Template, { ...defaultArgs, diagUrl: 'https://kroki.io/actdiag/svg/eNpVTjkOwkAM7POK0fa8AEFDQUGNKBCFCdbKItmVjCFIKH9nj4Sj81yeodYuQh6vBhhUjLFYo43hwWr5lJ48N0nsKDDuN9ZizfjMHVZw-8S5QtX88aMcEpbgYfw0d1oWT_n349myIQ9Q6qtWjePcuNN4lalynvVNbyYmN8Di_4fxDQA-Q4A=' }); 20 | export const WithLongDiagram = getComponenent(Template, { ...defaultArgs, diagUrl: 'https://kroki.io/plantuml/svg/eNplj0FvwjAMhe_5FVZP40CgaNMuUGkcdttp3Kc0NSVq4lRxGNKm_fe1HULuuD37-bOfuXPUm2QChEjRnlIMCDmdUfHNSYY6xh42a9Fsegflk-yYlOLlcHK2I2SGtX4WZm9sZ1o8uOzxxbuWAlIGj8cshs6M1jDuY2owyU2P8jAezdnn10j53X0hlBsZFW021Pq7HaVSNw-KN-OogG8F8BAGqT8dXhZjxW4cyJEW6kcC-yHWFagHqW0MfaThhYmaVyE26P_x27qaDmXeruqqAMMw1h-ZlRI4aF3dX7hOwm5XzfIKDctlNcshPT1tFa8JPYAj-Zf5F065sqM=' }); 21 | 22 | -------------------------------------------------------------------------------- /src/views/Window/Window.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Button, Modal } from 'semantic-ui-react' 3 | import PropTypes from 'prop-types'; 4 | 5 | import './Window.css' 6 | import classNames from 'classnames'; 7 | 8 | const Window = ({ title, children, open, isContentCentered, actions, headerButtons, headerComponent, onClose }) => { 9 | return 16 | 17 | {title} 18 | {headerButtons ? {headerButtons} : null} 19 | {headerComponent ? {headerComponent} : null} 20 | 21 | {children} 22 | 23 | {actions} 24 | 25 | 26 | 27 | } 28 | 29 | Window.propTypes = { 30 | title: PropTypes.string.isRequired, 31 | children: PropTypes.any.isRequired, 32 | open: PropTypes.bool.isRequired, 33 | isContentCentered: PropTypes.bool.isRequired, 34 | actions: PropTypes.element, 35 | onClose: PropTypes.func.isRequired, 36 | }; 37 | 38 | Window.defaultProps = { 39 | }; 40 | 41 | export default Window; 42 | -------------------------------------------------------------------------------- /src/views/CopyField/index.js: -------------------------------------------------------------------------------- 1 | import { copyButtonHovered, copyText } from '../../actions/editor' 2 | import { useDispatch, useSelector } from 'react-redux' 3 | import Internal from './CopyField' 4 | 5 | const CopyField = ({ scope }) => { 6 | const diagramEditUrl = useSelector((state) => state.editor.diagramEditUrl) 7 | const diagramUrl = useSelector((state) => state.editor.diagramUrl) 8 | const diagramText = useSelector((state) => state.editor.diagramText).replaceAll("-->","\\-\\-\\>") 9 | 10 | let text = ''; 11 | switch (scope) { 12 | case 'image': text = diagramUrl; break; 13 | case 'edit': text = diagramEditUrl; break; 14 | case 'markdown': text = `![Diagram](${diagramUrl})\n\n[Edit this diagram](${diagramEditUrl})\n`; break; 15 | case 'markdownsource': text = `![Diagram](${diagramUrl})\n\n\n\n[Edit this diagram](${diagramEditUrl})\n`; break; 16 | default: 17 | } 18 | const isMultiline = scope === 'markdown' || scope === 'markdownsource'; 19 | const isCopyHover = useSelector((state) => state.editor.scopes[scope]?.isHover) 20 | const isCopied = useSelector((state) => state.editor.scopes[scope]?.isCopied) 21 | const dispatch = useDispatch(); 22 | const onCopyHover = (scope, isHover) => dispatch(copyButtonHovered(scope, isHover)); 23 | const onCopy = (scope, text) => dispatch(copyText(scope, text)); 24 | 25 | return Internal({ text, scope, isCopyHover, isCopied, isMultiline, onCopyHover, onCopy }); 26 | } 27 | 28 | export default CopyField; 29 | -------------------------------------------------------------------------------- /src/views/WindowImportUrl/WindowImportUrl.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import WindowImportUrl from './WindowImportUrl'; 3 | import { getComponenent } from '../storybook/stories'; 4 | import { getReduxMockDecorator } from '../storybook/store'; 5 | 6 | export default { 7 | title: 'Components/WindowImportUrl', 8 | component: WindowImportUrl, 9 | }; 10 | 11 | const defaultState = { 12 | editor: { 13 | renderUrl: 'https://kroki.io/', 14 | windowImportUrlOpened: true, 15 | windowImportUrl: 'https://kroki.example.com/diagramX/data==', 16 | }, 17 | example: { 18 | windowExampleCardsOpened: false, 19 | windowExampleDetailsOpened: false, 20 | } 21 | }; 22 | 23 | const Template = (args) => { 24 | const { setState, decorator } = getReduxMockDecorator() 25 | let state = defaultState 26 | 27 | setState(state) 28 | return decorator(() => ) 29 | }; 30 | 31 | const defaultArgs = { 32 | open: true, 33 | url: '', 34 | }; 35 | 36 | export const Default = getComponenent(Template, { ...defaultArgs }); 37 | export const WithSomeData = getComponenent(Template, { ...defaultArgs, url: 'htt' }) 38 | export const WithLotsOfData = getComponenent(Template, { ...defaultArgs, url: 'https://kroki.io/graphviz/svg/eNp9kM0KgzAQhO8-xZIHaKlX6Ulp6S-FPoDEuGhqbML6cyl594JVGyv1OMPMt8ukMiNucojg5QFUTfKRQjVVjRSbzgZQPEEFW2An0oVkQWfO0mLTx53CHalF6hsAO6kwQV4PesbIRPylOJxIiwJp_YMDOPKWD8ouQP0F6AWp5DJ1qOyqU1w9Kte6NcZgjZPrLMxJl8imH9g_8_jzecLzYSzvteLPLBgR1rNvJ7Rwmw==' }) 39 | -------------------------------------------------------------------------------- /src/views/ExampleCard/ExampleCard.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | import './ExampleCard.css' 5 | import { Button, Card } from 'semantic-ui-react'; 6 | 7 | const ExampleCard = ({ diagType, description, diagUrl, onView, onImport }) => { 8 | if (!onView) { 9 | onView = (index) => {}; 10 | } 11 | return 12 |
13 | Diagram 14 |
15 | 16 | {diagType} 17 | {description} 18 | 19 | 20 | 21 | 22 | 23 |
24 | } 25 | 26 | ExampleCard.propTypes = { 27 | diagType: PropTypes.string.isRequired, 28 | description: PropTypes.string.isRequired, 29 | diagUrl: PropTypes.string.isRequired, 30 | onView: PropTypes.func.isRequired, 31 | onImport: PropTypes.func.isRequired, 32 | }; 33 | 34 | ExampleCard.defaultProps = { 35 | index: 1, 36 | diagType: 'Diagram Type', 37 | description: 'Description', 38 | diagUrl: 'https://kroki.io/blockdiag/svg/eNpdzDEKQjEQhOHeU4zpPYFoYesRxGJ9bwghMSsbUYJ4d10UCZbDfPynolOek0Q8FsDeNCestoisNLmy-Qg7R3Blcm5hPcr0ITdaB6X15fv-_YdJixo2CNHI2lmK3sPRA__RwV5SzV80ZAegJjXSyfMFptc71w==', 39 | }; 40 | 41 | export default ExampleCard; 42 | -------------------------------------------------------------------------------- /src/views/WindowExampleCards/WindowExampleCards.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import Window from '../Window' 3 | import ExampleCards from '../ExampleCards' 4 | import PropTypes from 'prop-types'; 5 | 6 | 7 | import './WindowExampleCards.css' 8 | import { Input } from 'semantic-ui-react'; 9 | 10 | const WindowExampleCards = ({ open, onClose, onSearchChange, search }) => { 11 | const onChange = (event, data) => onSearchChange(data.value); 12 | 13 | return 21 | open && element && element.focus()} 27 | onChange={onChange} 28 | value={search} 29 | /> 30 | 31 | 32 | } 33 | > 34 | 35 | 36 | } 37 | 38 | WindowExampleCards.propTypes = { 39 | open: PropTypes.bool.isRequired, 40 | onClose: PropTypes.func.isRequired, 41 | }; 42 | 43 | WindowExampleCards.defaultProps = { 44 | }; 45 | 46 | export default WindowExampleCards; -------------------------------------------------------------------------------- /src/views/ExampleDetail/ExampleDetail.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ExampleDetail from './ExampleDetail'; 3 | import { getComponenent } from '../storybook/stories'; 4 | 5 | export default { 6 | title: 'Components/ExampleDetail', 7 | component: ExampleDetail, 8 | }; 9 | 10 | const Template = (args) => ; 11 | 12 | const defaultArgs = { 13 | diagramType: 'Diagram Type', 14 | description: 'Description', 15 | diagUrl: 'https://kroki.io/blockdiag/svg/eNpdzDEKQjEQhOHeU4zpPYFoYesRxGJ9bwghMSsbUYJ4d10UCZbDfPynolOek0Q8FsDeNCestoisNLmy-Qg7R3Blcm5hPcr0ITdaB6X15fv-_YdJixo2CNHI2lmK3sPRA__RwV5SzV80ZAegJjXSyfMFptc71w==', 16 | diagramText: [ 17 | 'blockdiag {', 18 | ' Kroki -> generates -> "Block diagrams";', 19 | ' Kroki -> is -> "very easy!";', 20 | '', 21 | ' Kroki [color = "greenyellow"];', 22 | ' "Block diagrams" [color = "pink"];', 23 | ' "very easy!" [color = "orange"];', 24 | '}' 25 | ].join('\n'), 26 | items: [['Poide', 'Description'], ['Poide', 'Description'], ['Poide', 'Description'], ['Poide', 'Description'], ['Poide', 'Description'], ['Poide', 'Description'], ['Poide', 'Description'], ['Poide', 'Description'], ['Poide', 'Description'], ['Poide', 'Description'], ['Poide', 'Description'], ['Poide', 'Description'], ['Poide', 'Description'], ['Poide', 'Description'], ['Poide', 'Description']], 27 | itemIndex: 1, 28 | doc: 'https://example.com/this/is/url/for/doc', 29 | }; 30 | 31 | export const Default = getComponenent(Template, { ...defaultArgs }); 32 | export const WithoutDoc = getComponenent(Template, { ...defaultArgs, doc: undefined }); 33 | -------------------------------------------------------------------------------- /src/views/CopyField/CopyField.stories.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import CopyField from './CopyField'; 3 | import { Form } from 'semantic-ui-react'; 4 | import { getComponenent } from '../storybook/stories' 5 | 6 | export default { 7 | title: 'Components/CopyField', 8 | component: CopyField, 9 | }; 10 | 11 | const Template = (args) =>
; 12 | 13 | const defaultArgs = { 14 | text: 'https://kroki.example.com/grut/x4ettght46th==', 15 | isCopyHover: false, 16 | isCopied: false, 17 | scope: 'image', 18 | isMultiline: false, 19 | } 20 | 21 | export const Default = getComponenent(Template, { ...defaultArgs }) 22 | export const WithHover = getComponenent(Template, { ...defaultArgs, isCopyHover: true }) 23 | export const WithCopied = getComponenent(Template, { ...defaultArgs, isCopied: true }) 24 | export const WithHoverCopied = getComponenent(Template, { ...defaultArgs, isCopyHover: true, isCopied: true }) 25 | export const WithMultiline = getComponenent(Template, { ...defaultArgs, text: `line : "${defaultArgs.text}"\n\nOther line : '${defaultArgs.text}'`, isMultiline: true }) 26 | export const WithMultilineHover = getComponenent(Template, { ...defaultArgs, text: `line : "${defaultArgs.text}"\n\nOther line : '${defaultArgs.text}'`, isMultiline: true, isCopyHover: true }) 27 | export const WithMultilineHoverCopied = getComponenent(Template, { ...defaultArgs, text: `line : "${defaultArgs.text}"\n\nOther line : '${defaultArgs.text}'`, isMultiline: true, isCopyHover: true, isCopied: true }) 28 | export const WithLotOfLinesHoverCopied = getComponenent(Template, { ...defaultArgs, text: `line : "${defaultArgs.text}"\n\nOther line : '${defaultArgs.text}\n\ntext\n\n\nstill text\n.'`, isMultiline: true, isCopyHover: true, isCopied: true }) 29 | -------------------------------------------------------------------------------- /src/init/redux.js: -------------------------------------------------------------------------------- 1 | import { applyMiddleware, compose, createStore } from 'redux' 2 | import thunk from 'redux-thunk' 3 | import { routerMiddleware } from 'connected-react-router' 4 | import { createBrowserHistory } from 'history'; 5 | 6 | /** 7 | * @typedef {import('history').BrowserHistory} BrowserHistory 8 | */ 9 | /** 10 | * @typedef {import('redux').Reducer} Reducer 11 | * @template State 12 | */ 13 | /** 14 | * @typedef {import('redux').Store} Store 15 | * @template State 16 | */ 17 | /** 18 | * @typedef {import('redux').AnyAction} AnyAction 19 | */ 20 | 21 | /** 22 | * init a redux application 23 | * 24 | * @param {string} baseUrl 25 | * @param {State} initialState 26 | * @param {(history: BrowserHistory) => Reducer} reducerCreator 27 | * @return {{history: BrowserHistory, store: Store}} 28 | * @template State 29 | */ 30 | const initRedux = (baseUrl, initialState, reducerCreator) => { 31 | // Create browser history to use in the Redux store 32 | const history = createBrowserHistory({ basename: baseUrl }); 33 | const reducer = reducerCreator(history); 34 | 35 | const middleware = [ 36 | thunk, 37 | routerMiddleware(history), 38 | ] 39 | 40 | const enhancers = [] 41 | 42 | //const isDevelopment = process.env.NODE_ENV === 'development'; 43 | 44 | //if (isDevelopment && typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION__) { 45 | 46 | if (typeof window !== 'undefined' && window.__REDUX_DEVTOOLS_EXTENSION__) { 47 | enhancers.push(window.__REDUX_DEVTOOLS_EXTENSION__()); 48 | } 49 | 50 | const store = createStore( 51 | reducer, 52 | initialState, 53 | compose( 54 | applyMiddleware(...middleware), 55 | ...enhancers 56 | ) 57 | ); 58 | 59 | return { history, store }; 60 | }; 61 | export default initRedux; -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build run rm milestone-release release 2 | 3 | IMAGE_NAME=local-niolesk-image 4 | CONTAINER_NAME=local-niolesk 5 | 6 | VERSION_CHECKER=yarn info -R --json | grep "niolesk@workspace:." | jq -M '.children.Version' | sed -e 's/"//g' 7 | APP_VERSION=$(shell $(VERSION_CHECKER)) 8 | WORKING_DIR=$(shell pwd) 9 | 10 | PORT=8017 11 | 12 | rm: 13 | docker rm --force $(CONTAINER_NAME) || true 14 | 15 | run: rm 16 | docker run -d --rm=true -e "NIOLESK_KROKI_ENGINE=https://kroki.io/" -p $(PORT):80 --name "$(CONTAINER_NAME)" "$(IMAGE_NAME)" 17 | 18 | build: 19 | docker build --rm --force-rm -t "$(IMAGE_NAME)" . 20 | 21 | release: 22 | bash -c '! [[ `git status --porcelain` ]]' || (echo "You must have committed everything before running a release" && false) 23 | yarn version patch 24 | [ -f "doc/releases/v$$($(VERSION_CHECKER)).md" ] || (echo "You must have a release file for version [v$$($(VERSION_CHECKER))] before creating release" && git reset --hard && false) 25 | git add . 26 | git commit -m "v$$($(VERSION_CHECKER))" 27 | git tag "v$$($(VERSION_CHECKER))" 28 | yarn version patch 29 | git add . 30 | git commit -m "v$$($(VERSION_CHECKER)) : Start new developement" 31 | git push 32 | git push --tags 33 | 34 | milestone-release: 35 | bash -c '! [[ `git status --porcelain` ]]' || (echo "You must have committed everything before running a release" && false) 36 | yarn version minor 37 | [ -f "doc/releases/v$$($(VERSION_CHECKER)).md" ] || (echo "You must have a release file for version [v$$($(VERSION_CHECKER))] before creating release" && git reset --hard && false) 38 | git add . 39 | git commit -m "v$$($(VERSION_CHECKER))" 40 | git tag "v$$($(VERSION_CHECKER))" 41 | yarn version minor 42 | yarn version patch 43 | git add . 44 | git commit -m "v$$($(VERSION_CHECKER)) : Start new developement milestone" 45 | git push 46 | git push --tags 47 | 48 | info: 49 | @echo $(APP_VERSION) 50 | -------------------------------------------------------------------------------- /src/kroki/coder.test.js: -------------------------------------------------------------------------------- 1 | import { decode, encode } from "./coder" 2 | 3 | const exampleSets = [ 4 | { 5 | name: 'Basic', 6 | text: '"a" -> "b"', 7 | base64: 'eNpTSlRS0LVTUEpSAgAKxwH3', 8 | }, 9 | { 10 | name: 'With some non ascii char', 11 | text: '"a" -> "é"', 12 | base64: 'eNpTSlRS0LVTUDq8UgkADxEDAQ==', 13 | }, 14 | { 15 | name: 'With a CR at the end', 16 | text: '"a" -> "b"\n', 17 | base64: 'eNpTSlRS0LVTUEpS4gIADMgCAQ==', 18 | }, 19 | { 20 | name: 'With a CR in the middle', 21 | text: '"a" -> "b"\n"b" -> "c"', 22 | base64: 'eNpTSlRS0LVTUEpS4gJiMDNZCQArmgP5', 23 | }, 24 | { 25 | name: 'With a CR in the middle and at the end', 26 | text: '"a" -> "b"\n"b" -> "c"\n', 27 | base64: 'eNpTSlRS0LVTUEpS4gJiMDNZiQsAL50EAw==', 28 | }, 29 | { 30 | name: 'With some non ascii char, a CR in the middle and at the end', 31 | text: '"a" -> "é"\n"é" -> "c"\n', 32 | base64: 'eNpTSlRS0LVTUDq8UokLRIA5yUpcAE6zBhc=', 33 | }, 34 | { 35 | name: 'With an emoji', 36 | text: '"a" -> "🚆"\n"🚆" -> "c"', 37 | base64: 'eNpTSlRS0LVTUPowf1abEheEAgskKwEAeTEIkw==', 38 | }, 39 | ] 40 | 41 | describe('encode', ()=>{ 42 | const testExample = ({name, text, base64}) => { 43 | it(`should encode correctly for the test [${name}]`, () => { 44 | expect(encode(text)).toBe(base64); 45 | }) 46 | } 47 | exampleSets.forEach((testSet) => testExample(testSet)); 48 | }) 49 | 50 | describe('decode', ()=>{ 51 | const testExample = ({name, text, base64}) => { 52 | it(`should decode correctly for the test [${name}]`, () => { 53 | expect(decode(base64)).toBe(text); 54 | }) 55 | } 56 | exampleSets.forEach((testSet) => testExample(testSet)); 57 | }) 58 | 59 | -------------------------------------------------------------------------------- /create-example-cache.js: -------------------------------------------------------------------------------- 1 | import data from './src/examples/data' 2 | import { createHash } from 'crypto' 3 | import axios from 'axios' 4 | import util from 'util' 5 | import stream from 'stream' 6 | import fs from 'fs' 7 | 8 | const pipeline = util.promisify(stream.pipeline) 9 | const fileExists = (path) => new Promise((resolve, reject) => { 10 | fs.access(path, fs.F_OK, (err) => { 11 | if (err) { 12 | resolve(false); 13 | } else { 14 | resolve(true); 15 | } 16 | }) 17 | }) 18 | 19 | const writeToFile = (path, content) => { 20 | return new Promise((resolve, reject) => { 21 | const file = fs.createWriteStream(path); 22 | file.write(content); 23 | file.end(); 24 | file.on("finish", () => { resolve(true); }); 25 | file.on("error", reject); 26 | }); 27 | } 28 | 29 | const md5 = (s) => createHash('md5').update(s).digest('hex'); 30 | const engine = 'https://kroki.io' 31 | const dirname = './public/cache' 32 | 33 | const createCache = async (example) => { 34 | const ext = 'svg' 35 | const radical = [example.diagramType, 'svg', example.example].join('/') 36 | const url = `${engine}/${radical}` 37 | const sum = md5(radical) 38 | 39 | const response = await axios.get(url, { responseType: 'stream' }) 40 | const filename = `${sum}.${ext}` 41 | const fullFilename = `${dirname}/${filename}` 42 | if (! await fileExists(fullFilename)) { 43 | console.log(`${sum} : ${example.title} - ${example.description}`) 44 | await pipeline(response.data, fs.createWriteStream(fullFilename)) 45 | } 46 | return filename 47 | } 48 | 49 | const main = async () => { 50 | if (! await fileExists(dirname)) { 51 | await fs.promises.mkdir(dirname) 52 | } 53 | const cache = await Promise.all(data.map(createCache)) 54 | await writeToFile('./public/cache.js', `window.cache = ${JSON.stringify(cache.sort())};`) 55 | } 56 | 57 | main() -------------------------------------------------------------------------------- /src/views/CopyField/CopyField.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import classnames from 'classnames' 3 | import PropTypes from 'prop-types'; 4 | 5 | import './CopyField.css' 6 | import { Button, TextArea } from 'semantic-ui-react'; 7 | 8 | const CopyField = ({ text, onCopy, onCopyHover, isCopyHover, isCopied, scope, isMultiline }) => { 9 | if (!onCopy) { 10 | onCopy = (scope, text) => { }; 11 | } 12 | if (!onCopyHover) { 13 | onCopyHover = (scope, isHover) => { }; 14 | } 15 | 16 | return
17 |