Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41041b66ad | ||
|
|
2cbba5b5a2 | ||
|
|
1b66cc6c05 | ||
|
|
95640b8242 | ||
|
|
c355ff3f53 | ||
|
|
eb600c9acb |
No files matched your search
@@ -25,7 +25,7 @@ jobs:
|
||||
path: dist
|
||||
retention-days: 1
|
||||
- name: Get PR info
|
||||
uses: actions/github-script@v6.1.1
|
||||
uses: actions/github-script@v6.1.0
|
||||
with:
|
||||
script: |
|
||||
var fs = require('fs');
|
||||
|
||||
@@ -17,7 +17,7 @@ jobs:
|
||||
# workflow_run action (https://github.com/actions/download-artifact/issues/60)
|
||||
# so instead we get this mess:
|
||||
- name: Download artifact
|
||||
uses: actions/github-script@v6.1.1
|
||||
uses: actions/github-script@v6.1.0
|
||||
with:
|
||||
script: |
|
||||
var artifacts = await github.rest.actions.listWorkflowRunArtifacts({
|
||||
@@ -51,7 +51,7 @@ jobs:
|
||||
run: unzip -d dist previewbuild.zip && rm previewbuild.zip && unzip pr.json.zip && rm pr.json.zip
|
||||
- name: Read PR Info
|
||||
id: readctx
|
||||
uses: actions/github-script@v6.1.1
|
||||
uses: actions/github-script@v6.1.0
|
||||
with:
|
||||
script: |
|
||||
var fs = require('fs');
|
||||
|
||||
@@ -64,11 +64,10 @@ jobs:
|
||||
NETLIFY_DEPLOY_TO_PROD: true
|
||||
|
||||
push-to-dockerhub:
|
||||
name: Push Docker image to Docker Hub, ghcr
|
||||
name: Push Docker image to Docker Hub
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v3.0.2
|
||||
@@ -81,19 +80,11 @@ jobs:
|
||||
with:
|
||||
username: ${{ secrets.DOCKER_USERNAME }}
|
||||
password: ${{ secrets.DOCKER_PASSWORD }}
|
||||
- name: Login to the Container registry
|
||||
uses: docker/login-action@v2.0.0
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.actor }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
- name: Extract metadata (tags, labels) for Docker
|
||||
id: meta
|
||||
uses: docker/metadata-action@v4.0.1
|
||||
with:
|
||||
images: |
|
||||
${{ secrets.DOCKER_USERNAME }}/cinny
|
||||
ghcr.io/${{ github.repository }}
|
||||
images: ajbura/cinny
|
||||
- name: Build and push Docker image
|
||||
uses: docker/build-push-action@v3.1.1
|
||||
with:
|
||||
|
||||
+1
-6
@@ -36,9 +36,4 @@ It is not always possible to phrase every change in such a manner, but it is des
|
||||
|
||||
Also, we use [ESLint](https://eslint.org/) for clean and stylistically consistent code syntax, so make sure your pull request follow it.
|
||||
|
||||
**For any query or design discussion, join our [Matrix room](https://matrix.to/#/#cinny:matrix.org).**
|
||||
|
||||
## Helpful links
|
||||
- [BEM methodology](http://getbem.com/introduction/)
|
||||
- [Atomic design](https://bradfrost.com/blog/post/atomic-web-design/)
|
||||
- [Matrix JavaScript SDK documentation](https://matrix-org.github.io/matrix-js-sdk/19.2.0/index.html)
|
||||
**For any query or design discussion, join our [Matrix room](https://matrix.to/#/#cinny:matrix.org).**
|
||||
@@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2021-present Ajay Bura (ajbura)
|
||||
Copyright (c) 2021 Ajay Bura (ajbura)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# Cinny
|
||||
<p>
|
||||
<p align="center">
|
||||
<img src="https://raw.githubusercontent.com/ajbura/cinny/dev/public/res/svg/cinny.svg?sanitize=true"
|
||||
height="16">
|
||||
<span><b>Cinny</b></span>
|
||||
</p>
|
||||
<p align="center">
|
||||
<a href="https://github.com/ajbura/cinny/releases">
|
||||
<img alt="GitHub release downloads" src="https://img.shields.io/github/downloads/ajbura/cinny/total?logo=github&style=social"></a>
|
||||
<a href="https://hub.docker.com/r/ajbura/cinny">
|
||||
@@ -12,30 +16,21 @@
|
||||
<img alt="Sponsor Cinny" src="https://img.shields.io/opencollective/all/cinny?logo=opencollective&style=social"></a>
|
||||
</p>
|
||||
|
||||
A Matrix client focusing primarily on simple, elegant and secure interface. The main goal is to have an instant messaging application that is easy on people and has a modern touch.
|
||||
- [Roadmap](https://github.com/ajbura/cinny/projects/11)
|
||||
**Cinny** is a Matrix client focusing primarily on simple, elegant and secure interface. The main goal is to have a client that is easy on end user
|
||||
and feels a modern chat application.
|
||||
|
||||
- [Contributing](./CONTRIBUTING.md)
|
||||
- [Roadmap](https://github.com/ajbura/cinny/projects/11)
|
||||
|
||||
## Getting started
|
||||
Web app is available at https://app.cinny.in and gets updated on each new release. The `dev` branch is continuously deployed at https://dev.cinny.in but keep in mind that it could have things broken.
|
||||
## Building and Running
|
||||
|
||||
You can also download our desktop app from [cinny-desktop repository](https://github.com/cinnyapp/cinny-desktop).
|
||||
### Running pre-compiled
|
||||
|
||||
To host Cinny on your own, download tarball of the app from [GitHub release](https://github.com/cinnyapp/cinny/releases/latest).
|
||||
You can serve the application with a webserver of your choice by simply copying `dist/` directory to the webroot.
|
||||
To set default Homeserver on login and register page, place a customized [`config.json`](config.json) in webroot of your choice.
|
||||
|
||||
Alternatively you can just pull the [DockerHub image](https://hub.docker.com/r/ajbura/cinny) by:
|
||||
```
|
||||
docker pull ajbura/cinny
|
||||
```
|
||||
or [ghcr image](https://github.com/cinnyapp/cinny/pkgs/container/cinny) by:
|
||||
```
|
||||
docker pull ghcr.io/cinnyapp/cinny:latest
|
||||
```
|
||||
A tarball of pre-compiled version of the app is provided with each [release](https://github.com/ajbura/cinny/releases).
|
||||
You can serve the application with a webserver of your choosing by simply copying `dist/` directory to the webroot.
|
||||
|
||||
<details>
|
||||
<summary>PGP Public Key to verify tarball</summary>
|
||||
<summary>PGP Public Key to verify pre-compiled tarball</summary>
|
||||
|
||||
```
|
||||
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||
@@ -82,39 +77,48 @@ UeGsouhyuITLwEhScounZDqop+Dx
|
||||
```
|
||||
</details>
|
||||
|
||||
## Local development
|
||||
### Building from source
|
||||
> We recommend using a version manager as versions change very quickly. You will likely need to switch
|
||||
between multiple Node.js versions based on the needs of different projects you're working on. [NVM on windows](https://github.com/coreybutler/nvm-windows#installation--upgrades) on Windows and [nvm](https://github.com/nvm-sh/nvm) on Linux/macOS are pretty good choices. Also recommended nodejs version is 16.15.0 LTS.
|
||||
|
||||
Execute the following commands to start a development server:
|
||||
Execute the following commands to compile the app from its source code:
|
||||
|
||||
```sh
|
||||
npm ci # Installs all dependencies
|
||||
npm start # Serve a development version
|
||||
```
|
||||
|
||||
To build the app:
|
||||
```sh
|
||||
npm run build # Compiles the app into the dist/ directory
|
||||
```
|
||||
|
||||
You can then copy the files to a webserver's webroot of your choice.
|
||||
|
||||
To serve a development version of the app locally for testing, you need to use the command `npm start`.
|
||||
|
||||
### Running with Docker
|
||||
|
||||
This repository includes a Dockerfile, which builds the application from source and serves it with Nginx on port 80. To
|
||||
use this locally, you can build the container like so:
|
||||
|
||||
```
|
||||
docker build -t cinny:latest .
|
||||
```
|
||||
|
||||
You can then run the container you've built with a command similar to this:
|
||||
|
||||
```
|
||||
docker run -p 8080:80 cinny:latest
|
||||
```
|
||||
|
||||
This will forward your `localhost` port 8080 to the container's port 80. You can visit the app in your browser by navigating to `http://localhost:8080`.
|
||||
This will forward your `localhost` port 8080 to the container's port 80. You can visit the app in your browser by
|
||||
navigating to `http://localhost:8080`.
|
||||
|
||||
Alternatively you can just pull the [DockerHub image](https://hub.docker.com/r/ajbura/cinny) by `docker pull ajbura/cinny`.
|
||||
|
||||
### Configuring default Homeserver
|
||||
|
||||
To set default Homeserver on login and register page, place a customized [`config.json`](config.json) in webroot of your choice.
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2021-present Ajay Bura (ajbura)
|
||||
Copyright (c) 2021 Ajay Bura (ajbura)
|
||||
|
||||
Code licensed under the MIT License: <http://opensource.org/licenses/MIT>
|
||||
|
||||
|
||||
Generated
+1218
-98
File diff suppressed because it is too large.
Load diff
+11
-4
@@ -15,7 +15,8 @@
|
||||
"author": "Ajay Bura",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@khanacademy/simple-markdown": "^0.8.3",
|
||||
"@fontsource/inter": "^4.5.12",
|
||||
"@fontsource/roboto": "^4.5.8",
|
||||
"@matrix-org/olm": "https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.12.tgz",
|
||||
"@tippyjs/react": "^4.2.6",
|
||||
"babel-polyfill": "^6.26.0",
|
||||
@@ -31,6 +32,12 @@
|
||||
"linkify-html": "^4.0.0-beta.5",
|
||||
"linkifyjs": "^4.0.0-beta.5",
|
||||
"matrix-js-sdk": "^19.2.0",
|
||||
"micromark": "^3.0.10",
|
||||
"micromark-extension-gfm": "^2.0.1",
|
||||
"micromark-extension-math": "^2.0.2",
|
||||
"micromark-util-chunked": "^1.0.0",
|
||||
"micromark-util-resolve-all": "^1.0.0",
|
||||
"micromark-util-symbol": "^1.0.1",
|
||||
"prop-types": "^15.8.1",
|
||||
"react": "^17.0.2",
|
||||
"react-autosize-textarea": "^7.1.0",
|
||||
@@ -57,7 +64,7 @@
|
||||
"crypto-browserify": "^3.12.0",
|
||||
"css-loader": "^6.7.1",
|
||||
"css-minimizer-webpack-plugin": "^4.0.0",
|
||||
"eslint": "^8.22.0",
|
||||
"eslint": "^8.21.0",
|
||||
"eslint-config-airbnb": "^19.0.4",
|
||||
"eslint-plugin-import": "^2.26.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.6.1",
|
||||
@@ -69,7 +76,7 @@
|
||||
"html-webpack-plugin": "^5.3.1",
|
||||
"mini-css-extract-plugin": "^2.6.1",
|
||||
"path-browserify": "^1.0.1",
|
||||
"sass": "^1.54.4",
|
||||
"sass": "^1.54.3",
|
||||
"sass-loader": "^13.0.2",
|
||||
"stream-browserify": "^3.0.0",
|
||||
"style-loader": "^3.3.1",
|
||||
@@ -77,7 +84,7 @@
|
||||
"util": "^0.12.4",
|
||||
"webpack": "^5.74.0",
|
||||
"webpack-cli": "^4.10.0",
|
||||
"webpack-dev-server": "^4.10.0",
|
||||
"webpack-dev-server": "^4.9.3",
|
||||
"webpack-merge": "^5.7.3"
|
||||
}
|
||||
}
|
||||
@@ -1,41 +0,0 @@
|
||||
# Installing Webfonts
|
||||
Follow these simple Steps.
|
||||
|
||||
## 1.
|
||||
Put `supreme/` Folder into a Folder called `fonts/`.
|
||||
|
||||
## 2.
|
||||
Put `supreme.css` into your `css/` Folder.
|
||||
|
||||
## 3. (Optional)
|
||||
You may adapt the `url('path')` in `supreme.css` depends on your Website Filesystem.
|
||||
|
||||
## 4.
|
||||
Import `supreme.css` at the top of you main Stylesheet.
|
||||
|
||||
```
|
||||
@import url('supreme.css');
|
||||
```
|
||||
|
||||
## 5.
|
||||
|
||||
|
||||
```
|
||||
font-family: 'Supreme-Variable';
|
||||
font-family: 'Supreme-VariableItalic';
|
||||
font-family: 'Supreme-Thin';
|
||||
font-family: 'Supreme-ThinItalic';
|
||||
font-family: 'Supreme-Extralight';
|
||||
font-family: 'Supreme-ExtralightItalic';
|
||||
font-family: 'Supreme-Light';
|
||||
font-family: 'Supreme-LightItalic';
|
||||
font-family: 'Supreme-Regular';
|
||||
font-family: 'Supreme-Italic';
|
||||
font-family: 'Supreme-Medium';
|
||||
font-family: 'Supreme-MediumItalic';
|
||||
font-family: 'Supreme-Bold';
|
||||
font-family: 'Supreme-BoldItalic';
|
||||
font-family: 'Supreme-Extrabold';
|
||||
font-family: 'Supreme-ExtraboldItalic';
|
||||
```
|
||||
|
||||
@@ -1,175 +0,0 @@
|
||||
@font-face {
|
||||
font-family: 'Supreme-Variable';
|
||||
src: url('../fonts/Supreme-Variable.woff2') format('woff2'),
|
||||
url('../fonts/Supreme-Variable.woff') format('woff'),
|
||||
url('../fonts/Supreme-Variable.ttf') format('truetype');
|
||||
font-weight: 100 800;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'Supreme-VariableItalic';
|
||||
src: url('../fonts/Supreme-VariableItalic.woff2') format('woff2'),
|
||||
url('../fonts/Supreme-VariableItalic.woff') format('woff'),
|
||||
url('../fonts/Supreme-VariableItalic.ttf') format('truetype');
|
||||
font-weight: 100 800;
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'Supreme-Thin';
|
||||
src: url('../fonts/Supreme-Thin.woff2') format('woff2'),
|
||||
url('../fonts/Supreme-Thin.woff') format('woff'),
|
||||
url('../fonts/Supreme-Thin.ttf') format('truetype');
|
||||
font-weight: 100;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'Supreme-ThinItalic';
|
||||
src: url('../fonts/Supreme-ThinItalic.woff2') format('woff2'),
|
||||
url('../fonts/Supreme-ThinItalic.woff') format('woff'),
|
||||
url('../fonts/Supreme-ThinItalic.ttf') format('truetype');
|
||||
font-weight: 100;
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'Supreme-Extralight';
|
||||
src: url('../fonts/Supreme-Extralight.woff2') format('woff2'),
|
||||
url('../fonts/Supreme-Extralight.woff') format('woff'),
|
||||
url('../fonts/Supreme-Extralight.ttf') format('truetype');
|
||||
font-weight: 200;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'Supreme-ExtralightItalic';
|
||||
src: url('../fonts/Supreme-ExtralightItalic.woff2') format('woff2'),
|
||||
url('../fonts/Supreme-ExtralightItalic.woff') format('woff'),
|
||||
url('../fonts/Supreme-ExtralightItalic.ttf') format('truetype');
|
||||
font-weight: 200;
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'Supreme-Light';
|
||||
src: url('../fonts/Supreme-Light.woff2') format('woff2'),
|
||||
url('../fonts/Supreme-Light.woff') format('woff'),
|
||||
url('../fonts/Supreme-Light.ttf') format('truetype');
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'Supreme-LightItalic';
|
||||
src: url('../fonts/Supreme-LightItalic.woff2') format('woff2'),
|
||||
url('../fonts/Supreme-LightItalic.woff') format('woff'),
|
||||
url('../fonts/Supreme-LightItalic.ttf') format('truetype');
|
||||
font-weight: 300;
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'Supreme-Regular';
|
||||
src: url('../fonts/Supreme-Regular.woff2') format('woff2'),
|
||||
url('../fonts/Supreme-Regular.woff') format('woff'),
|
||||
url('../fonts/Supreme-Regular.ttf') format('truetype');
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'Supreme-Italic';
|
||||
src: url('../fonts/Supreme-Italic.woff2') format('woff2'),
|
||||
url('../fonts/Supreme-Italic.woff') format('woff'),
|
||||
url('../fonts/Supreme-Italic.ttf') format('truetype');
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'Supreme-Medium';
|
||||
src: url('../fonts/Supreme-Medium.woff2') format('woff2'),
|
||||
url('../fonts/Supreme-Medium.woff') format('woff'),
|
||||
url('../fonts/Supreme-Medium.ttf') format('truetype');
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'Supreme-MediumItalic';
|
||||
src: url('../fonts/Supreme-MediumItalic.woff2') format('woff2'),
|
||||
url('../fonts/Supreme-MediumItalic.woff') format('woff'),
|
||||
url('../fonts/Supreme-MediumItalic.ttf') format('truetype');
|
||||
font-weight: 500;
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'Supreme-Bold';
|
||||
src: url('../fonts/Supreme-Bold.woff2') format('woff2'),
|
||||
url('../fonts/Supreme-Bold.woff') format('woff'),
|
||||
url('../fonts/Supreme-Bold.ttf') format('truetype');
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'Supreme-BoldItalic';
|
||||
src: url('../fonts/Supreme-BoldItalic.woff2') format('woff2'),
|
||||
url('../fonts/Supreme-BoldItalic.woff') format('woff'),
|
||||
url('../fonts/Supreme-BoldItalic.ttf') format('truetype');
|
||||
font-weight: 700;
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'Supreme-Extrabold';
|
||||
src: url('../fonts/Supreme-Extrabold.woff2') format('woff2'),
|
||||
url('../fonts/Supreme-Extrabold.woff') format('woff'),
|
||||
url('../fonts/Supreme-Extrabold.ttf') format('truetype');
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
|
||||
@font-face {
|
||||
font-family: 'Supreme-ExtraboldItalic';
|
||||
src: url('../fonts/Supreme-ExtraboldItalic.woff2') format('woff2'),
|
||||
url('../fonts/Supreme-ExtraboldItalic.woff') format('woff'),
|
||||
url('../fonts/Supreme-ExtraboldItalic.ttf') format('truetype');
|
||||
font-weight: 800;
|
||||
font-display: swap;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@@ -1,4 +1,4 @@
|
||||
Copyright (c) 2021-present Ajay Bura (ajbura)
|
||||
Graphics (c) by Ajay Bura (ajbura)
|
||||
|
||||
Graphic content is licensed under a
|
||||
Creative Commons Attribution 4.0 International License.
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
--medium-modal-width: 712px;
|
||||
--large-modal-width: 1024px;
|
||||
|
||||
position: relative;
|
||||
|
||||
width: 100%;
|
||||
max-height: 100%;
|
||||
border-radius: var(--bo-radius);
|
||||
@@ -63,4 +63,4 @@
|
||||
100% {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,10 +38,10 @@
|
||||
.image-lightbox__content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
max-height: 80vh;
|
||||
max-height: 90vh;
|
||||
|
||||
& img {
|
||||
background-color: var(--bg-surface-low);
|
||||
background-color: black;
|
||||
object-fit: contain;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
|
||||
@@ -290,11 +290,6 @@ function MessageEdit({ body, onSave, onCancel }) {
|
||||
}, []);
|
||||
|
||||
const handleKeyDown = (e) => {
|
||||
if (e.key === 'Escape') {
|
||||
e.preventDefault();
|
||||
onCancel();
|
||||
}
|
||||
|
||||
if (e.key === 'Enter' && e.shiftKey === false) {
|
||||
e.preventDefault();
|
||||
onSave(editInputRef.current.value);
|
||||
@@ -714,9 +709,9 @@ function getEditedBody(editedMEvent) {
|
||||
}
|
||||
|
||||
function Message({
|
||||
mEvent, isBodyOnly, roomTimeline,
|
||||
focus, fullTime, isEdit, setEdit, cancelEdit,
|
||||
mEvent, isBodyOnly, roomTimeline, focus, fullTime,
|
||||
}) {
|
||||
const [isEditing, setIsEditing] = useState(false);
|
||||
const roomId = mEvent.getRoomId();
|
||||
const { editedTimeline, reactionTimeline } = roomTimeline ?? {};
|
||||
|
||||
@@ -731,7 +726,7 @@ function Message({
|
||||
const avatarSrc = mEvent.sender?.getAvatarUrl(initMatrix.matrixClient.baseUrl, 36, 36, 'crop') ?? null;
|
||||
|
||||
const edit = useCallback(() => {
|
||||
setEdit(eventId);
|
||||
setIsEditing(true);
|
||||
}, []);
|
||||
const reply = useCallback(() => {
|
||||
replyTo(senderId, mEvent.getId(), body);
|
||||
@@ -788,7 +783,7 @@ function Message({
|
||||
eventId={mEvent.replyEventId}
|
||||
/>
|
||||
)}
|
||||
{!isEdit && (
|
||||
{!isEditing && (
|
||||
<MessageBody
|
||||
senderName={username}
|
||||
isCustomHTML={isCustomHTML}
|
||||
@@ -797,22 +792,22 @@ function Message({
|
||||
isEdited={isEdited}
|
||||
/>
|
||||
)}
|
||||
{isEdit && (
|
||||
{isEditing && (
|
||||
<MessageEdit
|
||||
body={body}
|
||||
onSave={(newBody) => {
|
||||
if (newBody !== body) {
|
||||
initMatrix.roomsInput.sendEditedMessage(roomId, mEvent, newBody);
|
||||
}
|
||||
cancelEdit();
|
||||
setIsEditing(false);
|
||||
}}
|
||||
onCancel={cancelEdit}
|
||||
onCancel={() => setIsEditing(false)}
|
||||
/>
|
||||
)}
|
||||
{haveReactions && (
|
||||
<MessageReactionGroup roomTimeline={roomTimeline} mEvent={mEvent} />
|
||||
)}
|
||||
{roomTimeline && !isEdit && (
|
||||
{roomTimeline && !isEditing && (
|
||||
<MessageOptions
|
||||
roomTimeline={roomTimeline}
|
||||
mEvent={mEvent}
|
||||
@@ -829,9 +824,6 @@ Message.defaultProps = {
|
||||
focus: false,
|
||||
roomTimeline: null,
|
||||
fullTime: false,
|
||||
isEdit: false,
|
||||
setEdit: null,
|
||||
cancelEdit: null,
|
||||
};
|
||||
Message.propTypes = {
|
||||
mEvent: PropTypes.shape({}).isRequired,
|
||||
@@ -839,9 +831,6 @@ Message.propTypes = {
|
||||
roomTimeline: PropTypes.shape({}),
|
||||
focus: PropTypes.bool,
|
||||
fullTime: PropTypes.bool,
|
||||
isEdit: PropTypes.bool,
|
||||
setEdit: PropTypes.func,
|
||||
cancelEdit: PropTypes.func,
|
||||
};
|
||||
|
||||
export { Message, MessageReply, PlaceholderMessage };
|
||||
@@ -15,7 +15,7 @@
|
||||
display: flex;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
&__avatar-container {
|
||||
padding-top: 6px;
|
||||
@include dir.side(margin, 0, var(--sp-tight));
|
||||
@@ -101,6 +101,7 @@
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.message__header {
|
||||
display: flex;
|
||||
align-items: baseline;
|
||||
@@ -114,16 +115,10 @@
|
||||
@extend .cp-txt__ellipsis;
|
||||
color: inherit;
|
||||
}
|
||||
& > span:last-child {
|
||||
display: none;
|
||||
}
|
||||
& > span:last-child { display: none; }
|
||||
&:hover {
|
||||
& > span:first-child {
|
||||
display: none;
|
||||
}
|
||||
& > span:last-child {
|
||||
display: block;
|
||||
}
|
||||
& > span:first-child { display: none; }
|
||||
& > span:last-child { display: block; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,11 +168,7 @@
|
||||
& > .text > a {
|
||||
white-space: initial !important;
|
||||
}
|
||||
|
||||
& p:not(:last-child) {
|
||||
margin-bottom: var(--sp-normal);
|
||||
}
|
||||
|
||||
|
||||
& span[data-mx-pill] {
|
||||
background-color: hsla(0, 0%, 64%, 0.15);
|
||||
padding: 0 2px;
|
||||
@@ -200,7 +191,7 @@
|
||||
& span[data-mx-spoiler] {
|
||||
border-radius: 4px;
|
||||
background-color: rgba(124, 124, 124, 0.5);
|
||||
color: transparent;
|
||||
color:transparent;
|
||||
cursor: pointer;
|
||||
-webkit-touch-callout: none;
|
||||
-webkit-user-select: none;
|
||||
@@ -229,8 +220,6 @@
|
||||
padding: var(--sp-extra-tight) 0;
|
||||
&-btns button {
|
||||
margin: var(--sp-tight) 0 0 0;
|
||||
padding: var(--sp-ultra-tight) var(--sp-tight);
|
||||
min-width: 0;
|
||||
@include dir.side(margin, 0, var(--sp-tight));
|
||||
}
|
||||
}
|
||||
@@ -266,9 +255,9 @@
|
||||
}
|
||||
&-count {
|
||||
margin: 0 var(--sp-ultra-tight);
|
||||
color: var(--tc-surface-normal);
|
||||
color: var(--tc-surface-normal)
|
||||
}
|
||||
&-tooltip .react-emoji {
|
||||
&-tooltip .react-emoji {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin: 0 var(--sp-ultra-tight);
|
||||
@@ -281,19 +270,19 @@
|
||||
}
|
||||
}
|
||||
&:active {
|
||||
background-color: var(--bg-surface-active);
|
||||
background-color: var(--bg-surface-active)
|
||||
}
|
||||
|
||||
&--active {
|
||||
background-color: var(--bg-caution-active);
|
||||
|
||||
|
||||
@media (hover: hover) {
|
||||
&:hover {
|
||||
background-color: var(--bg-caution-hover);
|
||||
}
|
||||
}
|
||||
&:active {
|
||||
background-color: var(--bg-caution-active);
|
||||
background-color: var(--bg-caution-active)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -314,12 +303,7 @@
|
||||
|
||||
// markdown formating
|
||||
.message__body {
|
||||
& h1,
|
||||
h2,
|
||||
h3,
|
||||
h4,
|
||||
h5,
|
||||
h6 {
|
||||
& h1, h2, h3, h4, h5, h6 {
|
||||
margin: 0;
|
||||
margin-bottom: var(--sp-ultra-tight);
|
||||
font-weight: var(--fw-medium);
|
||||
@@ -440,8 +424,7 @@
|
||||
@include scrollbar.scroll__h;
|
||||
@include scrollbar.scroll--auto-hide;
|
||||
|
||||
& td,
|
||||
& th {
|
||||
& td, & th {
|
||||
padding: var(--sp-extra-tight);
|
||||
border: 1px solid var(--bg-surface-border);
|
||||
border-width: 0 1px 1px 0;
|
||||
@@ -449,11 +432,11 @@
|
||||
&:last-child {
|
||||
border-width: 0;
|
||||
border-bottom-width: 1px;
|
||||
[dir='rtl'] & {
|
||||
[dir=rtl] & {
|
||||
border-width: 0 1px 1px 0;
|
||||
}
|
||||
}
|
||||
[dir='rtl'] &:first-child {
|
||||
[dir=rtl] &:first-child {
|
||||
border-width: 0;
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
@@ -38,10 +38,10 @@ function SpaceOptions({ roomId, afterOptionSelect }) {
|
||||
|
||||
const handleMarkAsRead = () => {
|
||||
const spaceChildren = roomList.getCategorizedSpaces([roomId]);
|
||||
spaceChildren?.forEach((childIds) => {
|
||||
spaceChildren?.forEach((childIds, spaceId) => {
|
||||
childIds?.forEach((childId) => {
|
||||
markAsRead(childId);
|
||||
});
|
||||
})
|
||||
});
|
||||
afterOptionSelect();
|
||||
};
|
||||
|
||||
@@ -177,7 +177,7 @@ function getUserImagePack(mx) {
|
||||
}
|
||||
|
||||
const userImagePack = ImagePack.parsePack(mx.getUserId(), accountDataEmoji.event.content);
|
||||
if (userImagePack) userImagePack.displayName ??= 'Personal Emoji';
|
||||
userImagePack.displayName ??= 'Personal Emoji';
|
||||
return userImagePack;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import initMatrix from '../../../client/initMatrix';
|
||||
import cons from '../../../client/state/cons';
|
||||
import navigation from '../../../client/state/navigation';
|
||||
import { join } from '../../../client/action/room';
|
||||
import { selectRoom, selectTab } from '../../../client/action/navigation';
|
||||
import { selectRoom, selectSpace } from '../../../client/action/navigation';
|
||||
|
||||
import Text from '../../atoms/text/Text';
|
||||
import IconButton from '../../atoms/button/IconButton';
|
||||
@@ -32,7 +32,7 @@ function JoinAliasContent({ term, requestClose }) {
|
||||
const openRoom = (roomId) => {
|
||||
const room = mx.getRoom(roomId);
|
||||
if (!room) return;
|
||||
if (room.isSpaceRoom()) selectTab(roomId);
|
||||
if (room.isSpaceRoom()) selectSpace(roomId);
|
||||
else selectRoom(roomId);
|
||||
requestClose();
|
||||
};
|
||||
|
||||
@@ -6,7 +6,7 @@ import { twemojify } from '../../../util/twemojify';
|
||||
|
||||
import initMatrix from '../../../client/initMatrix';
|
||||
import cons from '../../../client/state/cons';
|
||||
import { selectTab, selectSpace } from '../../../client/action/navigation';
|
||||
import { selectSpace } from '../../../client/action/navigation';
|
||||
import navigation from '../../../client/state/navigation';
|
||||
import { abbreviateNumber } from '../../../util/common';
|
||||
|
||||
@@ -107,10 +107,7 @@ function DrawerBreadcrumb({ spaceId }) {
|
||||
{ index !== 0 && <RawIcon size="extra-small" src={ChevronRightIC} />}
|
||||
<Button
|
||||
className={index === spacePath.length - 1 ? 'drawer-breadcrumb__btn--selected' : ''}
|
||||
onClick={() => {
|
||||
if (id === cons.tabs.HOME) selectTab(id);
|
||||
else selectSpace(id);
|
||||
}}
|
||||
onClick={() => selectSpace(id)}
|
||||
>
|
||||
<Text variant="b2">{id === cons.tabs.HOME ? 'Home' : twemojify(mx.getRoom(id).name)}</Text>
|
||||
{ noti !== null && (
|
||||
|
||||
@@ -30,7 +30,7 @@ function Home({ spaceId }) {
|
||||
roomIds = spaceChildIds.filter((roomId) => rooms.has(roomId));
|
||||
directIds = spaceChildIds.filter((roomId) => directs.has(roomId));
|
||||
} else {
|
||||
spaceIds = roomList.getOrphanSpaces().filter((id) => !accountData.spaceShortcut.has(id));
|
||||
spaceIds = roomList.getOrphanSpaces();
|
||||
roomIds = roomList.getOrphanRooms();
|
||||
}
|
||||
|
||||
@@ -80,10 +80,10 @@ function Home({ spaceId }) {
|
||||
<RoomsCategory name="People" roomIds={directIds.sort(roomIdByActivity)} drawerPostie={drawerPostie} />
|
||||
)}
|
||||
|
||||
{ isCategorized && [...categories.keys()].sort(roomIdByAtoZ).map((catId) => {
|
||||
{ isCategorized && [...categories].map(([catId, childIds]) => {
|
||||
const rms = [];
|
||||
const dms = [];
|
||||
categories.get(catId).forEach((id) => {
|
||||
childIds.forEach((id) => {
|
||||
if (directs.has(id)) dms.push(id);
|
||||
else rms.push(id);
|
||||
});
|
||||
|
||||
@@ -118,15 +118,7 @@ function handleOnClickCapture(e) {
|
||||
}
|
||||
}
|
||||
|
||||
function renderEvent(
|
||||
roomTimeline,
|
||||
mEvent,
|
||||
prevMEvent,
|
||||
isFocus,
|
||||
isEdit,
|
||||
setEdit,
|
||||
cancelEdit,
|
||||
) {
|
||||
function renderEvent(roomTimeline, mEvent, prevMEvent, isFocus = false) {
|
||||
const isBodyOnly = (prevMEvent !== null
|
||||
&& prevMEvent.getSender() === mEvent.getSender()
|
||||
&& prevMEvent.getType() !== 'm.room.member'
|
||||
@@ -155,9 +147,6 @@ function renderEvent(
|
||||
roomTimeline={roomTimeline}
|
||||
focus={isFocus}
|
||||
fullTime={false}
|
||||
isEdit={isEdit}
|
||||
setEdit={setEdit}
|
||||
cancelEdit={cancelEdit}
|
||||
/>
|
||||
);
|
||||
}
|
||||
@@ -397,8 +386,6 @@ function RoomViewContent({ eventId, roomTimeline }) {
|
||||
const timelineSVRef = useRef(null);
|
||||
const timelineScrollRef = useRef(null);
|
||||
const eventLimitRef = useRef(null);
|
||||
const [editEventId, setEditEventId] = useState(null);
|
||||
const cancelEdit = () => setEditEventId(null);
|
||||
|
||||
const readUptoEvtStore = useStore(roomTimeline);
|
||||
const [onLimitUpdate, forceUpdateLimit] = useForceUpdate();
|
||||
@@ -483,42 +470,6 @@ function RoomViewContent({ eventId, roomTimeline }) {
|
||||
}
|
||||
}, [newEvent]);
|
||||
|
||||
const listenKeyboard = useCallback((event) => {
|
||||
if (event.ctrlKey || event.altKey || event.metaKey) return;
|
||||
if (event.key !== 'ArrowUp') return;
|
||||
if (navigation.isRawModalVisible) return;
|
||||
|
||||
if (document.activeElement.id !== 'message-textarea') return;
|
||||
if (document.activeElement.value !== '') return;
|
||||
|
||||
const {
|
||||
timeline: tl, activeTimeline, liveTimeline, matrixClient: mx,
|
||||
} = roomTimeline;
|
||||
const limit = eventLimitRef.current;
|
||||
if (activeTimeline !== liveTimeline) return;
|
||||
if (tl.length > limit.length) return;
|
||||
|
||||
const mTypes = ['m.text'];
|
||||
for (let i = tl.length - 1; i >= 0; i -= 1) {
|
||||
const mE = tl[i];
|
||||
if (
|
||||
mE.getSender() === mx.getUserId()
|
||||
&& mE.getType() === 'm.room.message'
|
||||
&& mTypes.includes(mE.getContent()?.msgtype)
|
||||
) {
|
||||
setEditEventId(mE.getId());
|
||||
return;
|
||||
}
|
||||
}
|
||||
}, [roomTimeline]);
|
||||
|
||||
useEffect(() => {
|
||||
document.body.addEventListener('keydown', listenKeyboard);
|
||||
return () => {
|
||||
document.body.removeEventListener('keydown', listenKeyboard);
|
||||
};
|
||||
}, [listenKeyboard]);
|
||||
|
||||
const handleTimelineScroll = (event) => {
|
||||
const timelineScroll = timelineScrollRef.current;
|
||||
if (!event.target) return;
|
||||
@@ -584,15 +535,7 @@ function RoomViewContent({ eventId, roomTimeline }) {
|
||||
const isFocus = focusId === mEvent.getId();
|
||||
if (isFocus) jumpToItemIndex = itemCountIndex;
|
||||
|
||||
tl.push(renderEvent(
|
||||
roomTimeline,
|
||||
mEvent,
|
||||
isNewEvent ? null : prevMEvent,
|
||||
isFocus,
|
||||
editEventId === mEvent.getId(),
|
||||
setEditEventId,
|
||||
cancelEdit,
|
||||
));
|
||||
tl.push(renderEvent(roomTimeline, mEvent, isNewEvent ? null : prevMEvent, isFocus));
|
||||
itemCountIndex += 1;
|
||||
}
|
||||
if (roomTimeline.canPaginateForward() || limit.length < timeline.length) {
|
||||
|
||||
@@ -70,7 +70,6 @@
|
||||
&__preview > img {
|
||||
max-height: 40px;
|
||||
border-radius: var(--bo-radius);
|
||||
max-width: 150px;
|
||||
}
|
||||
&__icon {
|
||||
padding: var(--sp-extra-tight);
|
||||
|
||||
@@ -4,17 +4,6 @@ import { selectTab, selectSpace, selectRoom } from '../action/navigation';
|
||||
|
||||
function initRoomListListener(roomList) {
|
||||
const listenRoomLeave = (roomId) => {
|
||||
const parents = roomList.roomIdToParents.get(roomId);
|
||||
|
||||
if (parents) {
|
||||
[...parents].forEach((pId) => {
|
||||
const data = navigation.spaceToRoom.get(pId);
|
||||
if (data?.roomId === roomId) {
|
||||
navigation.spaceToRoom.delete(pId);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (navigation.selectedRoomId === roomId) {
|
||||
selectRoom(null);
|
||||
}
|
||||
@@ -24,8 +13,6 @@ function initRoomListListener(roomList) {
|
||||
if (idIndex === 0) selectTab(cons.tabs.HOME);
|
||||
else selectSpace(navigation.selectedSpacePath[idIndex - 1]);
|
||||
}
|
||||
|
||||
navigation.removeRecentRoom(roomId);
|
||||
};
|
||||
|
||||
roomList.on(cons.events.roomList.ROOM_LEAVED, listenRoomLeave);
|
||||
|
||||
@@ -8,19 +8,12 @@ import AccountData from './state/AccountData';
|
||||
import RoomsInput from './state/RoomsInput';
|
||||
import Notifications from './state/Notifications';
|
||||
import { cryptoCallbacks } from './state/secretStorageKeys';
|
||||
import navigation from './state/navigation';
|
||||
|
||||
global.Olm = require('@matrix-org/olm');
|
||||
|
||||
// logger.disableAll();
|
||||
|
||||
class InitMatrix extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
|
||||
navigation.initMatrix = this;
|
||||
}
|
||||
|
||||
async init() {
|
||||
await this.startClient();
|
||||
this.setupSync();
|
||||
|
||||
@@ -89,7 +89,7 @@ class RoomList extends EventEmitter {
|
||||
|
||||
child.forEach((childId) => {
|
||||
const room = this.matrixClient.getRoom(childId);
|
||||
if (room === null || room.getMyMembership() !== 'join') return;
|
||||
if (room === null) return;
|
||||
if (room.isSpaceRoom()) categorizeSpace(childId);
|
||||
else mappedChild.add(childId);
|
||||
});
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
import EventEmitter from 'events';
|
||||
import { micromark } from 'micromark';
|
||||
import { gfm, gfmHtml } from 'micromark-extension-gfm';
|
||||
import encrypt from 'browser-encrypt-attachment';
|
||||
import { math } from 'micromark-extension-math';
|
||||
import { encode } from 'blurhash';
|
||||
import { getShortcodeToEmoji } from '../../app/organisms/emoji-board/custom-emoji';
|
||||
import { mathExtensionHtml, spoilerExtension, spoilerExtensionHtml } from '../../util/markdown';
|
||||
import { getBlobSafeMimeType } from '../../util/mimetypes';
|
||||
import { sanitizeText } from '../../util/sanitize';
|
||||
import cons from './cons';
|
||||
import settings from './settings';
|
||||
import { htmlOutput, parser } from '../../util/markdown';
|
||||
|
||||
const blurhashField = 'xyz.amorgan.blurhash';
|
||||
const MXID_REGEX = /\B@\S+:\S+\.\S+[^.,:;?!\s]/g;
|
||||
@@ -101,11 +104,14 @@ function getVideoThumbnail(video, width, height, mimeType) {
|
||||
}
|
||||
|
||||
function getFormattedBody(markdown) {
|
||||
let content = parser(markdown);
|
||||
if (content.length === 1 && content[0].type === 'paragraph') {
|
||||
content = content[0].content;
|
||||
}
|
||||
return htmlOutput(content);
|
||||
const result = micromark(markdown, {
|
||||
extensions: [gfm(), spoilerExtension(), math()],
|
||||
htmlExtensions: [gfmHtml(), spoilerExtensionHtml, mathExtensionHtml],
|
||||
});
|
||||
const bodyParts = result.match(/^(<p>)(.*)(<\/p>)$/);
|
||||
if (bodyParts === null) return result;
|
||||
if (bodyParts[2].indexOf('</p>') >= 0) return result;
|
||||
return bodyParts[2];
|
||||
}
|
||||
|
||||
function getReplyFormattedBody(roomId, reply) {
|
||||
|
||||
+32
-236
@@ -5,8 +5,6 @@ import cons from './cons';
|
||||
class Navigation extends EventEmitter {
|
||||
constructor() {
|
||||
super();
|
||||
// this will attached by initMatrix
|
||||
this.initMatrix = {};
|
||||
|
||||
this.selectedTab = cons.tabs.HOME;
|
||||
this.selectedSpaceId = null;
|
||||
@@ -16,20 +14,14 @@ class Navigation extends EventEmitter {
|
||||
this.isRoomSettings = false;
|
||||
this.recentRooms = [];
|
||||
|
||||
this.spaceToRoom = new Map();
|
||||
|
||||
this.rawModelStack = [];
|
||||
}
|
||||
|
||||
_addToSpacePath(roomId, asRoot) {
|
||||
if (typeof roomId !== 'string') {
|
||||
_setSpacePath(roomId) {
|
||||
if (roomId === null || roomId === cons.tabs.HOME) {
|
||||
this.selectedSpacePath = [cons.tabs.HOME];
|
||||
return;
|
||||
}
|
||||
if (asRoot) {
|
||||
this.selectedSpacePath = [roomId];
|
||||
return;
|
||||
}
|
||||
if (this.selectedSpacePath.includes(roomId)) {
|
||||
const spIndex = this.selectedSpacePath.indexOf(roomId);
|
||||
this.selectedSpacePath = this.selectedSpacePath.slice(0, spIndex + 1);
|
||||
@@ -38,223 +30,6 @@ class Navigation extends EventEmitter {
|
||||
this.selectedSpacePath.push(roomId);
|
||||
}
|
||||
|
||||
_mapRoomToSpace(roomId) {
|
||||
const { roomList, accountData } = this.initMatrix;
|
||||
if (
|
||||
this.selectedTab === cons.tabs.HOME
|
||||
&& roomList.rooms.has(roomId)
|
||||
&& !roomList.roomIdToParents.has(roomId)
|
||||
) {
|
||||
this.spaceToRoom.set(cons.tabs.HOME, {
|
||||
roomId,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
if (this.selectedTab === cons.tabs.DIRECTS && roomList.directs.has(roomId)) {
|
||||
this.spaceToRoom.set(cons.tabs.DIRECTS, {
|
||||
roomId,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
const parents = roomList.roomIdToParents.get(roomId);
|
||||
if (!parents) return;
|
||||
if (parents.has(this.selectedSpaceId)) {
|
||||
this.spaceToRoom.set(this.selectedSpaceId, {
|
||||
roomId,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
} else if (accountData.categorizedSpaces.has(this.selectedSpaceId)) {
|
||||
const categories = roomList.getCategorizedSpaces([this.selectedSpaceId]);
|
||||
const parent = [...parents].find((pId) => categories.has(pId));
|
||||
if (parent) {
|
||||
this.spaceToRoom.set(parent, {
|
||||
roomId,
|
||||
timestamp: Date.now(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
_selectRoom(roomId, eventId) {
|
||||
const prevSelectedRoomId = this.selectedRoomId;
|
||||
this.selectedRoomId = roomId;
|
||||
if (prevSelectedRoomId !== roomId) this._mapRoomToSpace(roomId);
|
||||
this.removeRecentRoom(prevSelectedRoomId);
|
||||
this.addRecentRoom(prevSelectedRoomId);
|
||||
this.removeRecentRoom(this.selectedRoomId);
|
||||
if (this.isRoomSettings && typeof this.selectedRoomId === 'string') {
|
||||
this.isRoomSettings = !this.isRoomSettings;
|
||||
this.emit(cons.events.navigation.ROOM_SETTINGS_TOGGLED, this.isRoomSettings);
|
||||
}
|
||||
this.emit(
|
||||
cons.events.navigation.ROOM_SELECTED,
|
||||
this.selectedRoomId,
|
||||
prevSelectedRoomId,
|
||||
eventId,
|
||||
);
|
||||
}
|
||||
|
||||
_selectTabWithRoom(roomId) {
|
||||
const { roomList, accountData } = this.initMatrix;
|
||||
const { categorizedSpaces } = accountData;
|
||||
|
||||
if (roomList.isOrphan(roomId)) {
|
||||
if (roomList.directs.has(roomId)) {
|
||||
this._selectSpace(null, true, false);
|
||||
this._selectTab(cons.tabs.DIRECTS, false);
|
||||
return;
|
||||
}
|
||||
this._selectSpace(null, true, false);
|
||||
this._selectTab(cons.tabs.HOME, false);
|
||||
return;
|
||||
}
|
||||
|
||||
const parents = roomList.roomIdToParents.get(roomId);
|
||||
|
||||
if (parents.has(this.selectedSpaceId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (categorizedSpaces.has(this.selectedSpaceId)) {
|
||||
const categories = roomList.getCategorizedSpaces([this.selectedSpaceId]);
|
||||
if ([...parents].find((pId) => categories.has(pId))) {
|
||||
// No need to select tab
|
||||
// As one of parent is child of selected categorized space.
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
const spaceInPath = [...this.selectedSpacePath].reverse().find((sId) => parents.has(sId));
|
||||
if (spaceInPath) {
|
||||
this._selectSpace(spaceInPath, false, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (roomList.directs.has(roomId)) {
|
||||
this._selectSpace(null, true, false);
|
||||
this._selectTab(cons.tabs.DIRECTS, false);
|
||||
return;
|
||||
}
|
||||
|
||||
if (parents.size > 0) {
|
||||
const sortedParents = [...parents].sort((p1, p2) => {
|
||||
const t1 = this.spaceToRoom.get(p1)?.timestamp ?? 0;
|
||||
const t2 = this.spaceToRoom.get(p2)?.timestamp ?? 0;
|
||||
return t2 - t1;
|
||||
});
|
||||
this._selectSpace(sortedParents[0], true, false);
|
||||
this._selectTab(sortedParents[0], false);
|
||||
}
|
||||
}
|
||||
|
||||
_getLatestActiveRoomId(roomIds) {
|
||||
const mx = this.initMatrix.matrixClient;
|
||||
|
||||
let ts = 0;
|
||||
let roomId = null;
|
||||
roomIds.forEach((childId) => {
|
||||
const room = mx.getRoom(childId);
|
||||
if (!room) return;
|
||||
const newTs = room.getLastActiveTimestamp();
|
||||
if (newTs > ts) {
|
||||
ts = newTs;
|
||||
roomId = childId;
|
||||
}
|
||||
});
|
||||
return roomId;
|
||||
}
|
||||
|
||||
_getLatestSelectedRoomId(spaceIds) {
|
||||
let ts = 0;
|
||||
let roomId = null;
|
||||
|
||||
spaceIds.forEach((sId) => {
|
||||
const data = this.spaceToRoom.get(sId);
|
||||
if (!data) return;
|
||||
const newTs = data.timestamp;
|
||||
if (newTs > ts) {
|
||||
ts = newTs;
|
||||
roomId = data.roomId;
|
||||
}
|
||||
});
|
||||
return roomId;
|
||||
}
|
||||
|
||||
_selectTab(tabId, selectRoom = true) {
|
||||
this.selectedTab = tabId;
|
||||
if (selectRoom) this._selectRoomWithTab(this.selectedTab);
|
||||
this.emit(cons.events.navigation.TAB_SELECTED, this.selectedTab);
|
||||
}
|
||||
|
||||
_selectSpace(roomId, asRoot, selectRoom = true) {
|
||||
this._addToSpacePath(roomId, asRoot);
|
||||
this.selectedSpaceId = roomId;
|
||||
if (!asRoot && selectRoom) this._selectRoomWithSpace(this.selectedSpaceId);
|
||||
this.emit(cons.events.navigation.SPACE_SELECTED, this.selectedSpaceId);
|
||||
}
|
||||
|
||||
_selectRoomWithSpace(spaceId) {
|
||||
if (!spaceId) return;
|
||||
const { roomList, accountData, matrixClient } = this.initMatrix;
|
||||
const { categorizedSpaces } = accountData;
|
||||
|
||||
const data = this.spaceToRoom.get(spaceId);
|
||||
if (data && !categorizedSpaces.has(spaceId)) {
|
||||
this._selectRoom(data.roomId);
|
||||
return;
|
||||
}
|
||||
|
||||
const children = [];
|
||||
|
||||
if (categorizedSpaces.has(spaceId)) {
|
||||
const categories = roomList.getCategorizedSpaces([spaceId]);
|
||||
|
||||
const latestSelectedRoom = this._getLatestSelectedRoomId([...categories.keys()]);
|
||||
|
||||
if (latestSelectedRoom) {
|
||||
this._selectRoom(latestSelectedRoom);
|
||||
return;
|
||||
}
|
||||
|
||||
categories?.forEach((categoryId) => {
|
||||
categoryId?.forEach((childId) => {
|
||||
children.push(childId);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
roomList.getSpaceChildren(spaceId).forEach((id) => {
|
||||
if (matrixClient.getRoom(id)?.isSpaceRoom() === false) {
|
||||
children.push(id);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (!children) {
|
||||
this._selectRoom(null);
|
||||
return;
|
||||
}
|
||||
|
||||
this._selectRoom(this._getLatestActiveRoomId(children));
|
||||
}
|
||||
|
||||
_selectRoomWithTab(tabId) {
|
||||
const { roomList } = this.initMatrix;
|
||||
if (tabId === cons.tabs.HOME || tabId === cons.tabs.DIRECTS) {
|
||||
const data = this.spaceToRoom.get(tabId);
|
||||
if (data) {
|
||||
this._selectRoom(data.roomId);
|
||||
return;
|
||||
}
|
||||
const children = tabId === cons.tabs.HOME ? roomList.getOrphanRooms() : [...roomList.directs];
|
||||
this._selectRoom(this._getLatestActiveRoomId(children));
|
||||
return;
|
||||
}
|
||||
this._selectRoomWithSpace(tabId);
|
||||
}
|
||||
|
||||
removeRecentRoom(roomId) {
|
||||
if (typeof roomId !== 'string') return;
|
||||
const roomIdIndex = this.recentRooms.indexOf(roomId);
|
||||
@@ -284,19 +59,40 @@ class Navigation extends EventEmitter {
|
||||
navigate(action) {
|
||||
const actions = {
|
||||
[cons.actions.navigation.SELECT_TAB]: () => {
|
||||
const roomId = (
|
||||
action.tabId !== cons.tabs.HOME && action.tabId !== cons.tabs.DIRECTS
|
||||
) ? action.tabId : null;
|
||||
|
||||
this._selectSpace(roomId, true);
|
||||
this._selectTab(action.tabId);
|
||||
this.selectedTab = action.tabId;
|
||||
if (this.selectedTab !== cons.tabs.DIRECTS) {
|
||||
if (this.selectedTab === cons.tabs.HOME) {
|
||||
this.selectedSpacePath = [cons.tabs.HOME];
|
||||
this.selectedSpaceId = null;
|
||||
} else {
|
||||
this.selectedSpacePath = [this.selectedTab];
|
||||
this.selectedSpaceId = this.selectedTab;
|
||||
}
|
||||
this.emit(cons.events.navigation.SPACE_SELECTED, this.selectedSpaceId);
|
||||
} else this.selectedSpaceId = null;
|
||||
this.emit(cons.events.navigation.TAB_SELECTED, this.selectedTab);
|
||||
},
|
||||
[cons.actions.navigation.SELECT_SPACE]: () => {
|
||||
this._selectSpace(action.roomId, false);
|
||||
this._setSpacePath(action.roomId);
|
||||
this.selectedSpaceId = action.roomId === cons.tabs.HOME ? null : action.roomId;
|
||||
this.emit(cons.events.navigation.SPACE_SELECTED, this.selectedSpaceId);
|
||||
},
|
||||
[cons.actions.navigation.SELECT_ROOM]: () => {
|
||||
if (action.roomId) this._selectTabWithRoom(action.roomId);
|
||||
this._selectRoom(action.roomId, action.eventId);
|
||||
const prevSelectedRoomId = this.selectedRoomId;
|
||||
this.selectedRoomId = action.roomId;
|
||||
this.removeRecentRoom(prevSelectedRoomId);
|
||||
this.addRecentRoom(prevSelectedRoomId);
|
||||
this.removeRecentRoom(this.selectedRoomId);
|
||||
if (this.isRoomSettings && typeof this.selectedRoomId === 'string') {
|
||||
this.isRoomSettings = !this.isRoomSettings;
|
||||
this.emit(cons.events.navigation.ROOM_SETTINGS_TOGGLED, this.isRoomSettings);
|
||||
}
|
||||
this.emit(
|
||||
cons.events.navigation.ROOM_SELECTED,
|
||||
this.selectedRoomId,
|
||||
prevSelectedRoomId,
|
||||
action.eventId,
|
||||
);
|
||||
},
|
||||
[cons.actions.navigation.OPEN_SPACE_SETTINGS]: () => {
|
||||
this.emit(cons.events.navigation.SPACE_SETTINGS_OPENED, action.roomId, action.tabText);
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
import '@fontsource/roboto/300.css';
|
||||
import '@fontsource/roboto/400.css';
|
||||
import '@fontsource/roboto/500.css';
|
||||
import '@fontsource/roboto/700.css';
|
||||
import '@fontsource/inter/variable.css';
|
||||
@@ -1,5 +1,6 @@
|
||||
import React from 'react';
|
||||
import ReactDom from 'react-dom';
|
||||
import './font';
|
||||
import './index.scss';
|
||||
|
||||
import settings from './client/state/settings';
|
||||
|
||||
+54
-16
@@ -1,5 +1,4 @@
|
||||
@use './app/partials/screen';
|
||||
@import '../public/font/supreme/css/supreme.css';
|
||||
|
||||
:root {
|
||||
|
||||
@@ -144,21 +143,21 @@
|
||||
--lh-s1: 24px;
|
||||
|
||||
--fs-b1: 16px;
|
||||
--ls-b1: 0;
|
||||
--ls-b1: 0.1px;
|
||||
--lh-b1: 24px;
|
||||
|
||||
--fs-b2: 14px;
|
||||
--ls-b2: 0;
|
||||
--ls-b2: 0.2px;
|
||||
--lh-b2: 20px;
|
||||
|
||||
--fs-b3: 12px;
|
||||
--ls-b3: 0;
|
||||
--ls-b3: 0px;
|
||||
--lh-b3: 16px;
|
||||
|
||||
/* font-weight */
|
||||
--fw-light: 300;
|
||||
--fw-normal: 440;
|
||||
--fw-medium: 600;
|
||||
--fw-normal: 400;
|
||||
--fw-medium: 500;
|
||||
--fw-bold: 700;
|
||||
|
||||
|
||||
@@ -192,10 +191,9 @@
|
||||
--fluid-push: cubic-bezier(0, 0.8, 0.67, 0.97);
|
||||
--fluid-slide-down: cubic-bezier(0.02, 0.82, 0.4, 0.96);
|
||||
--fluid-slide-up: cubic-bezier(0.13, 0.56, 0.25, 0.99);
|
||||
|
||||
--supreme-font: 'Supreme-Variable', 'Supreme-VariableItalic', 'Supreme-Thin', 'Supreme-ThinItalic', 'Supreme-Extralight', 'Supreme-ExtralightItalic', 'Supreme-Light', 'Supreme-LightItalic', 'Supreme-Regular', 'Supreme-Italic', 'Supreme-Medium', 'Supreme-MediumItalic', 'Supreme-Bold', 'Supreme-BoldItalic', 'Supreme-Extrabold', 'Supreme-ExtraboldItalic';
|
||||
--font-primary: var(--supreme-font), Arial, sans-serif;
|
||||
--font-secondary: var(--font-primary);
|
||||
|
||||
--font-primary: 'Roboto', sans-serif;
|
||||
--font-secondary: 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
|
||||
@@ -276,9 +274,25 @@
|
||||
|
||||
--bs-primary-border: inset 0 0 0 1px var(--bg-primary-border);
|
||||
--bs-primary-outline: 0 0 0 2px var(--bg-primary-border);
|
||||
|
||||
/* font styles: font-size, letter-spacing, line-hight */
|
||||
--fs-h1: 35.6px;
|
||||
|
||||
--fs-h2: 23.6px;
|
||||
|
||||
--fs-s1: 17.6px;
|
||||
|
||||
--fs-b1: 14.6px;
|
||||
--ls-b1: 0.14px;
|
||||
|
||||
--fs-b2: 13.2px;
|
||||
|
||||
--fs-b3: 11.2px;
|
||||
|
||||
/* override normal font weight for dark mode */
|
||||
--fw-normal: 340;
|
||||
--fw-normal: 350;
|
||||
|
||||
--font-secondary: 'InterVariable', 'Roboto', sans-serif;
|
||||
}
|
||||
|
||||
.dark-theme,
|
||||
@@ -321,10 +335,34 @@
|
||||
font-family: var(--font-primary);
|
||||
|
||||
/* override font styles for primary font */
|
||||
--fw-light: 400;
|
||||
--fw-normal: 500;
|
||||
--fw-medium: 600;
|
||||
--fw-bold: 700;
|
||||
--fs-h1: 36px;
|
||||
--ls-h1: -1.5px;
|
||||
--lh-h1: 38px;
|
||||
|
||||
--fs-h2: 24px;
|
||||
--ls-h2: -0.5px;
|
||||
--lh-h2: 30px;
|
||||
|
||||
--fs-s1: 18px;
|
||||
--ls-s1: -0.2px;
|
||||
--lh-s1: 24px;
|
||||
|
||||
--fs-b1: 16px;
|
||||
--ls-b1: 0.1px;
|
||||
--lh-b1: 24px;
|
||||
|
||||
--fs-b2: 14px;
|
||||
--ls-b2: 0.2px;
|
||||
--lh-b2: 20px;
|
||||
|
||||
--fs-b3: 12px;
|
||||
--ls-b3: 0px;
|
||||
--lh-b3: 16px;
|
||||
|
||||
--fw-light: 300;
|
||||
--fw-normal: 400;
|
||||
--fw-medium: 500;
|
||||
--fw-bold: 600;
|
||||
}
|
||||
|
||||
html {
|
||||
@@ -342,7 +380,7 @@ body {
|
||||
background-color: var(--bg-surface-low);
|
||||
|
||||
/*Why font-variant-ligatures => https://github.com/rsms/inter/issues/222 */
|
||||
font-variant-ligatures: no-common-ligatures;
|
||||
font-variant-ligatures: no-contextual;
|
||||
}
|
||||
#root {
|
||||
width: 100%;
|
||||
|
||||
+187
-78
@@ -1,89 +1,198 @@
|
||||
import SimpleMarkdown from '@khanacademy/simple-markdown';
|
||||
/* eslint-disable no-param-reassign */
|
||||
/* eslint-disable no-plusplus */
|
||||
/* eslint-disable no-continue */
|
||||
|
||||
const {
|
||||
defaultRules, parserFor, outputFor, anyScopeRegex, blockRegex, inlineRegex, htmlTag, sanitizeText,
|
||||
} = SimpleMarkdown;
|
||||
import { codes } from 'micromark-util-symbol/codes';
|
||||
import { types } from 'micromark-util-symbol/types';
|
||||
import { resolveAll } from 'micromark-util-resolve-all';
|
||||
import { splice } from 'micromark-util-chunked';
|
||||
|
||||
function mathHtml(wrap, node) {
|
||||
return htmlTag(wrap, htmlTag('code', sanitizeText(node.content)), { 'data-mx-maths': node.content });
|
||||
function inlineExtension(marker, len, key) {
|
||||
const keySeq = `${key}Sequence`;
|
||||
const keySeqTmp = `${keySeq}Temporary`;
|
||||
|
||||
return () => {
|
||||
function tokenize(effects, ok, nok) {
|
||||
const { previous, events } = this;
|
||||
|
||||
let size = 0;
|
||||
|
||||
function more(code) {
|
||||
// consume more markers if the maximum length hasn't been reached yet
|
||||
if (code === marker && size < len) {
|
||||
effects.consume(code);
|
||||
size += 1;
|
||||
return more;
|
||||
}
|
||||
|
||||
// check for minimum length
|
||||
if (size < len) return nok(code);
|
||||
|
||||
effects.exit(keySeqTmp);
|
||||
return ok(code);
|
||||
}
|
||||
|
||||
function start(code) {
|
||||
// ignore code if it's not a marker
|
||||
if (code !== marker) return nok(code);
|
||||
|
||||
if (previous === marker
|
||||
&& events[events.length - 1][1].type !== types.characterEscape) return nok(code);
|
||||
|
||||
effects.enter(keySeqTmp);
|
||||
return more(code);
|
||||
}
|
||||
|
||||
return start;
|
||||
}
|
||||
|
||||
function resolve(events, context) {
|
||||
let i = -1;
|
||||
|
||||
while (++i < events.length) {
|
||||
if (events[i][0] !== 'enter' || events[i][1].type !== keySeqTmp) continue;
|
||||
|
||||
let open = i;
|
||||
while (open--) {
|
||||
if (events[open][0] !== 'exit' || events[open][1].type !== keySeqTmp) continue;
|
||||
|
||||
events[i][1].type = keySeq;
|
||||
events[open][1].type = keySeq;
|
||||
|
||||
const border = {
|
||||
type: key,
|
||||
start: { ...events[open][1].start },
|
||||
end: { ...events[i][1].end },
|
||||
};
|
||||
|
||||
const text = {
|
||||
type: `${key}Text`,
|
||||
start: { ...events[open][1].end },
|
||||
end: { ...events[i][1].start },
|
||||
};
|
||||
|
||||
const nextEvents = [
|
||||
['enter', border, context],
|
||||
['enter', events[open][1], context],
|
||||
['exit', events[open][1], context],
|
||||
['enter', text, context],
|
||||
];
|
||||
|
||||
splice(
|
||||
nextEvents,
|
||||
nextEvents.length,
|
||||
0,
|
||||
resolveAll(
|
||||
context.parser.constructs.insideSpan.null,
|
||||
events.slice(open + 1, i),
|
||||
context,
|
||||
),
|
||||
);
|
||||
|
||||
splice(nextEvents, nextEvents.length, 0, [
|
||||
['exit', text, context],
|
||||
['enter', events[i][1], context],
|
||||
['exit', events[i][1], context],
|
||||
['exit', border, context],
|
||||
]);
|
||||
|
||||
splice(events, open - 1, i - open + 3, nextEvents);
|
||||
|
||||
i = open + nextEvents.length - 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
events.forEach((event) => {
|
||||
if (event[1].type === keySeqTmp) {
|
||||
event[1].type = types.data;
|
||||
}
|
||||
});
|
||||
|
||||
return events;
|
||||
}
|
||||
|
||||
const tokenizer = {
|
||||
tokenize,
|
||||
resolveAll: resolve,
|
||||
};
|
||||
|
||||
return {
|
||||
text: { [marker]: tokenizer },
|
||||
insideSpan: { null: [tokenizer] },
|
||||
attentionMarkers: { null: [marker] },
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
const rules = {
|
||||
...defaultRules,
|
||||
Array: {
|
||||
...defaultRules.Array,
|
||||
plain: (arr, output, state) => arr.map((node) => output(node, state)).join(''),
|
||||
const spoilerExtension = inlineExtension(codes.verticalBar, 2, 'spoiler');
|
||||
|
||||
const spoilerExtensionHtml = {
|
||||
enter: {
|
||||
spoiler() {
|
||||
this.tag('<span data-mx-spoiler>');
|
||||
},
|
||||
},
|
||||
displayMath: {
|
||||
order: defaultRules.list.order + 0.5,
|
||||
match: blockRegex(/^\$\$\n*([\s\S]+?)\n*\$\$/),
|
||||
parse: (capture) => ({ content: capture[1] }),
|
||||
plain: (node) => `$$\n${node.content}\n$$`,
|
||||
html: (node) => mathHtml('div', node),
|
||||
},
|
||||
newline: {
|
||||
...defaultRules.newline,
|
||||
plain: () => '\n',
|
||||
},
|
||||
paragraph: {
|
||||
...defaultRules.paragraph,
|
||||
plain: (node, output, state) => `${output(node.content, state)}\n\n`,
|
||||
html: (node, output, state) => htmlTag('p', output(node.content, state)),
|
||||
},
|
||||
escape: {
|
||||
...defaultRules.escape,
|
||||
plain: (node, output, state) => `\\${output(node.content, state)}`,
|
||||
},
|
||||
em: {
|
||||
...defaultRules.em,
|
||||
plain: (node, output, state) => `_${output(node.content, state)}_`,
|
||||
},
|
||||
strong: {
|
||||
...defaultRules.strong,
|
||||
plain: (node, output, state) => `**${output(node.content, state)}**`,
|
||||
},
|
||||
u: {
|
||||
...defaultRules.u,
|
||||
plain: (node, output, state) => `__${output(node.content, state)}__`,
|
||||
},
|
||||
del: {
|
||||
...defaultRules.del,
|
||||
plain: (node, output, state) => `~~${output(node.content, state)}~~`,
|
||||
},
|
||||
spoiler: {
|
||||
order: defaultRules.em.order - 0.5,
|
||||
match: inlineRegex(/^\|\|([\s\S]+?)\|\|(?:\(([\s\S]+?)\))?/),
|
||||
parse: (capture, parse, state) => ({
|
||||
content: parse(capture[1], state),
|
||||
reason: capture[2],
|
||||
}),
|
||||
plain: (node) => `[spoiler${node.reason ? `: ${node.reason}` : ''}](mxc://somewhere)`,
|
||||
html: (node, output, state) => `<span data-mx-spoiler${node.reason ? `="${sanitizeText(node.reason)}"` : ''}>${output(node.content, state)}</span>`,
|
||||
},
|
||||
inlineMath: {
|
||||
order: defaultRules.del.order + 0.5,
|
||||
match: inlineRegex(/^\$(\S[\s\S]+?\S|\S)\$(?!\d)/),
|
||||
parse: (capture) => ({ content: capture[1] }),
|
||||
plain: (node) => `$${node.content}$`,
|
||||
html: (node) => mathHtml('span', node),
|
||||
},
|
||||
br: {
|
||||
...defaultRules.br,
|
||||
match: anyScopeRegex(/^ *\n/),
|
||||
plain: () => '\n',
|
||||
},
|
||||
text: {
|
||||
...defaultRules.text,
|
||||
match: anyScopeRegex(/^[\s\S]+?(?=[^0-9A-Za-z\s\u00c0-\uffff]| *\n|\w+:\S|$)/),
|
||||
plain: (node) => node.content,
|
||||
exit: {
|
||||
spoiler() {
|
||||
this.tag('</span>');
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const parser = parserFor(rules);
|
||||
|
||||
const plainOutput = outputFor(rules, 'plain');
|
||||
const htmlOutput = outputFor(rules, 'html');
|
||||
const mathExtensionHtml = {
|
||||
enter: {
|
||||
mathFlow() {
|
||||
this.lineEndingIfNeeded();
|
||||
},
|
||||
mathFlowFenceMeta() {
|
||||
this.buffer();
|
||||
},
|
||||
mathText() {
|
||||
this.buffer();
|
||||
},
|
||||
},
|
||||
exit: {
|
||||
mathFlow() {
|
||||
const value = this.encode(this.resume().replace(/(?:\r?\n|\r)$/, ''));
|
||||
this.tag('<div data-mx-maths="');
|
||||
this.tag(value);
|
||||
this.tag('"><code>');
|
||||
this.raw(value);
|
||||
this.tag('</code></div>');
|
||||
this.setData('mathFlowOpen');
|
||||
this.setData('slurpOneLineEnding');
|
||||
},
|
||||
mathFlowFence() {
|
||||
// After the first fence.
|
||||
if (!this.getData('mathFlowOpen')) {
|
||||
this.setData('mathFlowOpen', true);
|
||||
this.setData('slurpOneLineEnding', true);
|
||||
this.buffer();
|
||||
}
|
||||
},
|
||||
mathFlowFenceMeta() {
|
||||
this.resume();
|
||||
},
|
||||
mathFlowValue(token) {
|
||||
this.raw(this.sliceSerialize(token));
|
||||
},
|
||||
mathText() {
|
||||
const value = this.encode(this.resume());
|
||||
this.tag('<span data-mx-maths="');
|
||||
this.tag(value);
|
||||
this.tag('"><code>');
|
||||
this.raw(value);
|
||||
this.tag('</code></span>');
|
||||
},
|
||||
mathTextData(token) {
|
||||
this.raw(this.sliceSerialize(token));
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export {
|
||||
parser, plainOutput, htmlOutput,
|
||||
inlineExtension,
|
||||
spoilerExtension, spoilerExtensionHtml,
|
||||
mathExtensionHtml,
|
||||
};
|
||||
@@ -19,7 +19,7 @@ const permittedTagToAttributes = {
|
||||
div: ['data-mx-maths'],
|
||||
a: ['name', 'target', 'href', 'rel'],
|
||||
img: ['width', 'height', 'alt', 'title', 'src', 'data-mx-emoticon'],
|
||||
ol: ['start'],
|
||||
o: ['start'],
|
||||
code: ['class'],
|
||||
};
|
||||
|
||||
|
||||
Reference in new issue
Block a user