Commit d14e3e12 authored by kiraacorsac's avatar kiraacorsac
Browse files

init

parents
Pipeline #115406 passed with stage
in 2 minutes and 7 seconds
node_modules
dist
playground/data
# Add if you are working with new file types
*.pdb
*.csv
*.gff
*.bed
*.txt
image: node:latest
pages:
stage: deploy
tags:
- shared-fi
script:
- mkdir public
- cd app
- npm ci
- npm run build
- cp -r ./dist/* ../public
artifacts:
paths:
- public
only:
- main
## Chromazoom
Chromatin visualisation app + library
### Development
#### Supported browsers:
- Google Chrome Canary
#### Project initialization
Windows 10 (Creator's update and later):
- Enable 'Developer mode'
- Clone with symlinks enabled
`git clone -c core.symlinks=true`
Run dev server
- `cd app`
- `npm run serve`
### Pages
Pushing to `main` branch triggers publish on https://visitlab.pages.fi.muni.cz/chromatin/chromazoom/
{
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended",
"plugin:react-hooks/recommended"
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaFeatures": {
"jsx": true
},
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": [
"react",
"@typescript-eslint"
],
"rules": {
}
}
node_modules
dist
\ No newline at end of file
image: node:latest
pages:
stage: deploy
tags:
- shared-fi
script:
- mkdir public
- cd app
- npm install
- npm run build
- cp -r ./dist/* ../public
artifacts:
paths:
- public
only:
- main
{
"CurrentProjectSetting": null
}
\ No newline at end of file
{
"ExpandedNodes": [
"",
"\\src",
"\\src\\RightPanel"
],
"SelectedNode": "\\src\\RightPanel\\ChromatinViewportConfiguration.tsx",
"PreviewInSolutionExplorer": false
}
\ No newline at end of file
{
"Version": 1,
"ProjectMap": {
"a2fe74e1-b743-11d0-ae1a-00a0c90fffc3": {
"ProjectGuid": "a2fe74e1-b743-11d0-ae1a-00a0c90fffc3",
"DisplayName": "Miscellaneous Files",
"ColorIndex": -1
}
},
"NextColorIndex": 0
}
\ No newline at end of file
This diff is collapsed.
{
"name": "chromazoom",
"version": "1.0.0",
"type": "module",
"main": "./dist/main.js",
"typings": "./dist/index.d.ts",
"repository": "https://gitlab.fi.muni.cz/visitlab/chromatin/chromazoom.git",
"author": "Kiraa Corsac <kiraa@mail.muni.cz>",
"license": "MIT",
"scripts": {
"serve": "webpack serve --hot --config webpack.development.mjs",
"serve-production": "webpack serve --config webpack.production.mjs",
"build": "webpack --config webpack.production.mjs"
},
"dependencies": {
"@gmod/gff": "^1.1.2",
"@types/chroma-js": "^2.1.3",
"chroma-js": "^2.4.2",
"d3": "^7.2.1",
"file-saver": "^2.0.5",
"flexlayout-react": "^0.6",
"gl-matrix": "^3.3.0",
"graphology": "^0.23.2",
"graphology-layout-forceatlas2": "^0.8.1",
"graphology-types": "^0.23.0",
"idb-keyval": "^6.0.3",
"image-js": "^0.33.1",
"immer": "^9.0.6",
"lodash": "^4.17.21",
"newtype-ts": "^0.3.5",
"papaparse": "^5.3.1",
"parse-pdb": "^1.0.0",
"randomcolor": "^0.6.2",
"react": "^17.0.2",
"react-complex-tree": "^1.1.1",
"react-dom": "^17.0.2",
"react-fast-compare": "^3.2.0",
"react-hot-keys": "^2.7.1",
"react-redux": "^7.2.4",
"react-sigma-v2": "^1.3.0",
"react-use": "^17.3.1",
"rooks": "^5.7.3",
"sigma": "^2.2.0",
"simple-statistics": "^7.7.0",
"stream": "^0.0.2",
"use-deep-compare-effect": "^1.8.1"
},
"devDependencies": {
"@fluentui/react": "^8.30.3",
"@fluentui/react-icons": "^1.1.145",
"@fluentui/svg-icons": "^1.1.139",
"@minoru/react-dnd-treeview": "^1.5.3",
"@pmmmwh/react-refresh-webpack-plugin": "^0.5.1",
"@react-hook/mouse-position": "^4.1.1",
"@reduxjs/toolkit": "^1.6.1",
"@thi.ng/color": "^3.2.7",
"@types/d3": "^7.1.0",
"@types/file-saver": "^2.0.4",
"@types/lodash": "^4.14.172",
"@types/papaparse": "^5.2.6",
"@types/randomcolor": "^0.5.6",
"@types/react": "^17.0.19",
"@types/react-dom": "^17.0.9",
"@types/react-redux": "^7.1.18",
"@types/resize-observer-browser": "^0.1.6",
"@typescript-eslint/eslint-plugin": "^4.30.0",
"@typescript-eslint/parser": "^4.30.0",
"@webgpu/types": "^0.1.13",
"css-loader": "^6.2.0",
"eslint": "^7.32.0",
"eslint-plugin-react": "^7.25.1",
"eslint-plugin-react-hooks": "^4.3.0",
"fork-ts-checker-webpack-plugin": "^6.4.0",
"html-webpack-plugin": "^5.3.2",
"react-refresh": "^0.10.0",
"react-refresh-typescript": "^2.0.2",
"sass": "^1.39.0",
"sass-loader": "^12.1.0",
"style-loader": "^3.2.1",
"svg-inline-loader": "^0.8.2",
"ts-loader": "^9.2.5",
"type-fest": "^2.5.1",
"typescript": "^4.4.2",
"webpack": "^5.52.0",
"webpack-cli": "^4.8.0",
"webpack-dev-server": "^4.1.0",
"webpack-merge": "^5.8.0"
}
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link rel="stylesheet" href="https://unpkg.com/higlass@1.11.3/dist/hglib.css">
<title>Webpack</title>
<script defer src="main.js"></script>
</head>
<body>
<div id="root"></div>
</body>
</html>
\ No newline at end of file
@import '@fluentui/react/dist/sass/References';
ul, li {
list-style-type: none;
}
.App {
display: flex;
flex-direction: column;
}
.topPanel {
flex-grow: 0;
flex-basis: 44px;
border-bottom: 1px solid $ms-color-gray140;
}
.mainArea {
flex: 1;
display: flex;
flex-direction: row;
overflow: hidden;
}
.flexlayout__layout {
flex: 1;
position: relative;
overflow: hidden;
}
.flexlayout__splitter {
background-color: #1a1a1a;
}
@media (hover: hover) {
.flexlayout__splitter:hover {
background-color: #333333;
}
}
.flexlayout__splitter_border {
z-index: 10;
}
.flexlayout__splitter_drag {
z-index: 1000;
border-radius: 5px;
background-color: #404040;
}
.flexlayout__outline_rect {
position: absolute;
cursor: move;
box-sizing: border-box;
border: 2px solid #cfe8ff;
box-shadow: inset 0 0 60px rgba(0, 0, 0, 0.2);
border-radius: 5px;
z-index: 1000;
}
.flexlayout__outline_rect_edge {
cursor: move;
border: 2px solid #b7d1b5;
box-shadow: inset 0 0 60px rgba(0, 0, 0, 0.2);
border-radius: 5px;
z-index: 1000;
box-sizing: border-box;
}
.flexlayout__edge_rect {
position: absolute;
z-index: 1000;
box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
background-color: gray;
}
.flexlayout__drag_rect {
position: absolute;
cursor: move;
color: white;
background-color: #121212;
border: 2px solid #333333;
box-shadow: inset 0 0 60px rgba(0, 0, 0, 0.3);
border-radius: 5px;
z-index: 1000;
box-sizing: border-box;
opacity: 0.9;
text-align: center;
display: flex;
justify-content: center;
flex-direction: column;
overflow: hidden;
padding: 10px;
word-wrap: break-word;
font-size: medium;
font-family: Roboto, Arial, sans-serif;
}
.flexlayout__tabset {
overflow: hidden;
background: none;
}
.flexlayout__tabset_header {
}
.flexlayout__tabset_header_content {
flex-grow: 1;
}
.flexlayout__tabset_tabbar_outer {
box-sizing: border-box;
position: absolute;
left: 0;
right: 0;
overflow: hidden;
display: flex;
background: none;
}
.flexlayout__tabset_tabbar_outer_top {
/* border-bottom: 1px solid #262626; */
}
.flexlayout__tabset_tabbar_outer_bottom {
/* border-top: 1px solid #262626; */
}
.flexlayout__tabset_tabbar_inner {
position: relative;
box-sizing: border-box;
display: flex;
flex-grow: 1;
overflow: hidden;
}
.flexlayout__tabset_tabbar_inner_tab_container {
display: flex;
box-sizing: border-box;
position: absolute;
top: 0;
bottom: 0;
width: 10000px;
}
.flexlayout__tabset_tabbar_inner_tab_container_top {
}
.flexlayout__tabset_tabbar_inner_tab_container_bottom {
}
.flexlayout__tabset-selected {
// background-color: #111c25;
}
.flexlayout__tabset-maximized {
}
.flexlayout__tab {
overflow: hidden;
position: absolute;
box-sizing: border-box;
}
.flexlayout__tab_button {
display: inline-flex;
align-items: center;
box-sizing: border-box;
cursor: pointer;
padding: 0px 8px 0px 8px;
margin-right: 8px;
background-color: #2f2f2f;
}
.flexlayout__tabset-selected .flexlayout__tab_button--selected {
color: white;
background-color: #6db1e8;
}
@media (hover: hover) {
.flexlayout__tab_button:hover {
}
}
.flexlayout__tab_button--unselected {
color: gray;
}
.flexlayout__tab_button_top {
}
.flexlayout__tab_button_bottom {
}
.flexlayout__tab_button_leading {
display: inline-block;
}
.flexlayout__tab_button_content {
font-size: $ms-font-size-14;
font-weight: $ms-font-weight-semibold;
display: inline-block;
}
.flexlayout__tab_button_textbox {
border: none;
/* color: green;
background-color: #262626; */
}
.flexlayout__tab_button_textbox:focus {
outline: none;
}
.flexlayout__tab_button_trailing {
margin-left: 8px;
}
.flexlayout__tab_button--selected .flexlayout__tab_button_trailing {
color: white;
}
.flexlayout__tab_button_trailing, .flexlayout__tab_button_trailing i {
width: 8px;
height: 8px;
font-size: 8px;
}
.flexlayout__splitter {
background-color: $ms-color-gray140;
}
.toolOptionsPanel {
width: 100%;
height: 44px;
background-color: $ms-color-gray190;
border-bottom: 1px solid $ms-color-gray150;
justify-content: center;
}
\ No newline at end of file
import './App.scss';
import React, { useEffect, useReducer } from 'react';
import TopBar from './components/TopBar';
import { Icon, Label, Separator, Slider, Stack } from '@fluentui/react';
import { useState } from 'react';
import * as FlexLayout from "flexlayout-react";
import { IIcons } from 'flexlayout-react/declarations/view/Layout';
import { initialWorkspaceState, WorkspaceActionKind, workspaceReducer, WorkspaceStorage } from './modules/storage/models/workspace';
import { RightPanel } from './components/RightPanel';
import { GraphicsLibrary } from "./modules/graphics/index";
import { ChromatinViewport } from './components/viewports/ChromatinViewport';
import { D1Viewport } from './components/viewports/D1Viewport';
import { ConfigurationActionKind, configurationReducer, ConfigurationState, defaultChromatinViewportConfiguration, defaultForceGraphViewportConfiguration, defaultDistanceViewportConfiguration, ViewportConfigurationType } from './modules/storage/models/viewports';
import { TADViewport } from './components/viewports/TADViewport';
import { isNumber } from 'lodash';
import { CursorClick24Regular, Lasso24Regular } from '@fluentui/react-icons';
import { NewXYZDataDialog } from './components/dialogs/NewXYZDataDialog';
// import New1DDataDialog from './dialogs/New1DDataDialog';
import { DataAction, DataActionKind, dataReducer, DataState } from './modules/storage/models/data';
import { SelectionActionKind, selectionReducer, SelectionState } from './modules/storage/models/selections';
import { clearBrowser, loadFromBrowser, saveToBrowser } from './modules/storage/inBrowserStorage';
import { saveToFile, loadFromFile } from './modules/storage/fileStorage';
import { ImportWorkspaceDialog } from './components/dialogs/ImportWorkspaceDialog';
import { ApplicationState, APPLICATION_STATE_VERSION } from './modules/storage/state';
import { ForceGraphViewport } from './components/viewports/ForceGraphViewport';
import { ToolsList } from './components/Tools/ToolsList';
import { ToolOptions } from './components/Tools/ToolOptions';
import { NewGenomicDataDialog } from './components/dialogs/NewGenomicDataDialog';
export enum Tool {
PointSelection = 0,
SphereSelection = 1,
}
function App(): JSX.Element {
const [adapter, setAdapter] = useState<GPUAdapter | null>(null);
const [device, setDevice] = useState<GPUDevice | null>(null);
const [deviceError, setDeviceError] = useState<GPUUncapturedErrorEvent | null>(null);
const [graphicsLibrary, setGraphicsLibrary] = useState<GraphicsLibrary | null>(null);
//#region Adapter, Device, Library Initialization
useEffect(() => {
async function waitForAdapter() {
const adapter = await navigator.gpu.requestAdapter({
powerPreference: "high-performance",
});
setAdapter(adapter);
}
waitForAdapter();
}, []);
useEffect(() => {
if (adapter == null) {
return;
}
const waitForDevice = async function () {
const device = await adapter.requestDevice({
requiredFeatures: ['timestamp-query']
});
device.onuncapturederror = (error: GPUUncapturedErrorEvent) => {
setDeviceError(error);
};
setDeviceError(null);
setDevice(device);
}
waitForDevice();
}, [adapter]);
useEffect(() => {
if (adapter == null || device == null) {
return;
}
setGraphicsLibrary(() => new GraphicsLibrary(adapter, device));
}, [adapter, device]);
//#endregion
//#region Application Data
const initialState: ApplicationState = {
version: APPLICATION_STATE_VERSION,
data: {
dataMaxId: 0,
data: [],
},
configurations: {
maxId: 0,
configurations: [],
},
selections: {
maxId: 0,
selections: [],
},
workspace: {
workspaceData: initialWorkspaceState
}
}
const [data, dispatchData] = useReducer(
dataReducer,
initialState.data as DataState
);
const [workspace, dispatchWorkspace] = useReducer(
workspaceReducer,
initialState.workspace?.workspaceData as FlexLayout.IJsonModel
);
const [configurations, dispatchConfigurations] = useReducer(
configurationReducer,
initialState.configurations as ConfigurationState
);
const [selections, dispatchSelections] = useReducer(
selectionReducer,
initialState.selections as SelectionState
);
function setCurrentState(state: ApplicationState) {
if (state.version == null || state.version < APPLICATION_STATE_VERSION) {
throw "The workspace you are trying to load was created in a version of Chromazoom that is no longer supported."
}
if (state.data)
dispatchData({
type: DataActionKind.SET,
state: state.data
});
if (state.workspace)
dispatchWorkspace({
type: WorkspaceActionKind.REPLACE,
model: state.workspace.workspaceData
});
if (state.configurations)
dispatchConfigurations({
type: ConfigurationActionKind.SET,
state: state.configurations
});
if (state.selections)
dispatchSelections({
type: SelectionActionKind.SET,
state: state.selections
});
}
function getCurrentState(): ApplicationState {
return {
data,
configurations,
<