Skip to content

Commit

Permalink
Merge pull request #566 from tuanchauict/release/alpha/1.3
Browse files Browse the repository at this point in the history
Version 1.3: Snapping line to a box
  • Loading branch information
tuanchauict authored Jul 7, 2023
2 parents ba9a0a3 + b848408 commit 5e01d31
Show file tree
Hide file tree
Showing 55 changed files with 1,809 additions and 305 deletions.
13 changes: 13 additions & 0 deletions .github/FUNDING.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# These are supported funding model platforms

github: tuanchauict
# patreon: # Replace with a single Patreon username
# open_collective: # Replace with a single Open Collective username
ko_fi: monosketch
# tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
# community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
# liberapay: # Replace with a single Liberapay username
# issuehunt: # Replace with a single IssueHunt username
# otechie: # Replace with a single Otechie username
# lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
# custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
51 changes: 26 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
[![License](https://img.shields.io/badge/License-Apache_2.0-blue.svg)][apache2.0] [![Kotlin](https://img.shields.io/badge/kotlin-%237F52FF.svg?style=flat&logo=kotlin&logoColor=white)][KotlinJS] [![SASS](https://img.shields.io/badge/SASS-hotpink.svg?style=flat&logo=SASS&logoColor=white)][sass]


# What is it?

Mono Sketch is a client-side only web based sketch tool for drawing *ASCII diagrams*. You can use
Expand All @@ -10,35 +9,33 @@ the app at [app.monosketch.io][app].
┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
Edge Region 1 │
────────────── send msg to ──────────────
/\_/\ │ │─────────────┴ websocket │─────────────┴
( o.o ) ◀══════════▶┤┌─────────────┴◀═══════════▶└┤┌─────────────┴
> ^ < │ ┤ Envoy │ sub to this ┤Gateway server│
──────────────┘ channel ───────▲──────
─ ─ ─ ─ ─ ─ ─ ║ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘
────────────── send msg to ──────────────
/\_/\ │ │─────────────┴ websocket │─────────────┴
( o.o ) ◀══════════╰┤╭─────────────┴◀═══════════▶╰┤╭─────────────┴
> ^ < │ ┤ Envoy │ sub to this ┤Gateway server│
───────────── channel ───────▲──────
─ ─ ─ ─ ─ ─ ─ ║ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘
/\_/\ ║ │
( o.o ) ◀═══════════════════╝ │ send msg to all GS subs
> ^ < └───────────────────────┐
─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
Main region
┌──────────────┐ ┌──────────────┐ ┌─────────────┐
│┌─────────────┴┐ send │┌─────────────┴┐ │┌──────────┼──┴┐
└───── ──▶└┤┌─────────────┴┐ channel msg └┤┌─────────────┴┐ └┤┌────────────┴┐│
└┤ Webapp ├──────────────▶ Admin Server ├───────────▶┤Channel Server│
└───────┬──────┘ └──────────────┘ route to └──────────────┘
│ store channel
▼ message server
░░░░░░░░
░Vitess░
░░░░░░░░
─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
│ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
Main region │
┌──────────────┐ ┌──────────────┐ ┌─────────────┐
│┌─────────────┴┐ send │┌─────────────┴┐ │┌─────────────┴┐ │
└───────▶└┤┌─────────────┴┐ channel msg └┤┌─────────────┴┐ └┤┌─────────────┴┐
└┤ Webapp ├──────────────▶ Admin Server ├───────────┴▶Channel Server
└───────┬──────┘ └──────────────┘ route to └──────────────┘
│ store channel
▼ message server
░░░░░░░░
░Vitess░
░░░░░░░░
─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─
```

<a href="https://www.buymeacoffee.com/monosketchapp" target="_blank"><img src="https://www.buymeacoffee.com/assets/img/guidelines/download-assets-sm-2.svg" alt="Buy Me A Coffee" style="height: 48px !important;" ></a>

# Features

## Supporting features
Expand Down Expand Up @@ -125,7 +122,11 @@ pipenv run dev
```

[apache2.0]: https://opensource.org/licenses/Apache-2.0

[app]: https://app.monosketch.io/

[KotlinJS]: https://kotlinlang.org/docs/js-overview.html

[Pipenv]: https://pipenv.pypa.io/en/latest/

[sass]: https://sass-lang.com/
1 change: 0 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ plugins {
}

group = "com.monosketch"
version = "1.1.0-alpha"

allprojects {
ext {
Expand Down
22 changes: 15 additions & 7 deletions libs/graphicsgeo/src/main/kotlin/mono/graphics.geo/MousePointer.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,42 @@ package mono.graphics.geo
* A sealed class which indicates mouse event pointer types.
*/
sealed interface MousePointer {
object Idle : MousePointer
val boardCoordinate: Point

object Idle : MousePointer {
override val boardCoordinate: Point
get() = Point.ZERO
}

data class Move(
val boardCoordinate: Point,
override val boardCoordinate: Point,
val pointPx: Point
) : MousePointer

data class Down(
val boardCoordinate: Point,
override val boardCoordinate: Point,
val pointPx: Point,
val isWithShiftKey: Boolean
) : MousePointer

data class Drag(
val mouseDownPoint: Point,
val boardCoordinate: Point,
override val boardCoordinate: Point,
val boardCoordinateF: PointF,
val isWithShiftKey: Boolean
) : MousePointer

data class Up(
val mouseDownPoint: Point,
val boardCoordinate: Point,
override val boardCoordinate: Point,
val boardCoordinateF: PointF,
val isWithShiftKey: Boolean
) : MousePointer

data class Click(val boardCoordinate: Point, val isWithShiftKey: Boolean) : MousePointer
data class Click(
override val boardCoordinate: Point,
val isWithShiftKey: Boolean
) : MousePointer

data class DoubleClick(val boardCoordinate: Point) : MousePointer
data class DoubleClick(override val boardCoordinate: Point) : MousePointer
}
10 changes: 10 additions & 0 deletions libs/graphicsgeo/src/main/kotlin/mono/graphics.geo/Rect.kt
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ data class Rect(
return isHorizontalOverlap && isVerticalOverlap
}

/**
* Returns true if the point is one of its vertices.
*/
fun isVertex(point: Point): Boolean {
val isOnVerticalEdge = point.left == left || point.left == right
val isOnHorizontalEdge = point.top == top || point.top == bottom

return isOnHorizontalEdge && isOnVerticalEdge
}

override fun toString(): String = "[$left, $top] - [$width x $height]"

internal object RectSerializer : KSerializer<Rect> {
Expand Down
3 changes: 2 additions & 1 deletion libs/monoboard/src/main/kotlin/mono/graphics/board/Pixel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -63,5 +63,6 @@ class Pixel(
enum class Highlight {
NO,
SELECTED,
TEXT_EDITING
TEXT_EDITING,
LINE_CONNECT_FOCUSING
}
1 change: 1 addition & 0 deletions libs/shape-clipboard/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

plugins {
kotlin("js")
kotlin("plugin.serialization")
}

repositories {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
package mono.shape.clipboard

import kotlinx.browser.document
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
Expand All @@ -14,6 +15,7 @@ import mono.livedata.LiveData
import mono.livedata.MutableLiveData
import mono.shape.extra.TextExtra
import mono.shape.serialization.AbstractSerializableShape
import mono.shape.serialization.SerializableLineConnector
import mono.shape.serialization.SerializableText
import org.w3c.dom.HTMLElement

Expand All @@ -23,10 +25,8 @@ import org.w3c.dom.HTMLElement
*/
class ShapeClipboardManager(private val body: HTMLElement) {

private val clipboardShapeMutableLiveData: MutableLiveData<List<AbstractSerializableShape>> =
MutableLiveData(emptyList())
val clipboardShapeLiveData: LiveData<List<AbstractSerializableShape>> =
clipboardShapeMutableLiveData
private val clipboardShapeMutableLiveData = MutableLiveData(ClipboardObject(emptyList()))
val clipboardShapeLiveData: LiveData<ClipboardObject> = clipboardShapeMutableLiveData

init {
document.onpaste = {
Expand All @@ -40,12 +40,28 @@ class ShapeClipboardManager(private val body: HTMLElement) {
if (text.isBlank()) {
return
}
clipboardShapeMutableLiveData.value =
try {
Json.decodeFromString(text)
} catch (e: Exception) {
listOf(createTextShapeFromText(text))
}
val clipboardObject = try {
Json.decodeFromString<ClipboardObject>(text)
} catch (e: Exception) {
null
}
if (clipboardObject != null) {
clipboardShapeMutableLiveData.value = clipboardObject
return
}

// This is for backward compatibility with old clipboard format
val clipboardShapes = try {
Json.decodeFromString<List<AbstractSerializableShape>>(text)
} catch (e: Exception) {
null
}
if (clipboardShapes != null) {
clipboardShapeMutableLiveData.value = ClipboardObject(clipboardShapes)
return
}

clipboardShapeMutableLiveData.value = ClipboardObject(listOf(createTextShapeFromText(text)))
}

private fun createTextShapeFromText(text: String): SerializableText {
Expand All @@ -64,10 +80,8 @@ class ShapeClipboardManager(private val body: HTMLElement) {
)
}

fun setClipboard(shapes: List<AbstractSerializableShape>) {
val json = Json.encodeToString(shapes)
setClipboardText(json)
}
fun setClipboard(clipboardObject: ClipboardObject) =
setClipboardText(Json.encodeToString(clipboardObject))

fun setClipboardText(text: String) {
TextArea(body, classes = "hidden", content = text) {
Expand All @@ -77,6 +91,17 @@ class ShapeClipboardManager(private val body: HTMLElement) {
}
}

/**
* A data class to store shapes and connectors in clipboard when serializing to JSON.
*/
@Serializable
data class ClipboardObject(
val shapes: List<AbstractSerializableShape>,
val connectors: List<SerializableLineConnector>
) {
constructor(shapes: List<AbstractSerializableShape>) : this(shapes, emptyList())
}

companion object {
private const val DEFAULT_TEXT_BOUND_WIDTH = 400
private const val NON_BREAKING_SPACE_CHAR = '\u00a0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ class SelectedShapeManager {
val selectedShapes: Set<AbstractShape>
get() = selectedShapesLiveData.value

private val focusingShapeMutableLiveData: MutableLiveData<FocusingShape?> =
MutableLiveData(null)
val focusingShapeLiveData: LiveData<FocusingShape?> = focusingShapeMutableLiveData

fun addSelectedShape(shape: AbstractShape) {
selectedShapesMutableLiveData.value += shape
}
Expand All @@ -36,4 +40,28 @@ class SelectedShapeManager {
fun clearSelectedShapes() {
selectedShapesMutableLiveData.value = emptySet()
}

fun setFocusingShape(shape: AbstractShape?, focusType: ShapeFocusType) {
focusingShapeMutableLiveData.value =
if (shape == null) null else FocusingShape(shape, focusType)
}

fun getFocusingType(shape: AbstractShape?): ShapeFocusType? =
if (shape == focusingShapeLiveData.value?.shape) {
focusingShapeLiveData.value?.focusType
} else {
null
}

/**
* An enum class defines the type of focus.
*/
enum class ShapeFocusType {
LINE_CONNECTING
}

/**
* A model class to store the focusing shape and its focusing type.
*/
data class FocusingShape(val shape: AbstractShape, val focusType: ShapeFocusType)
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,29 @@ import kotlinx.serialization.Serializable
import mono.common.currentTimeMillis
import mono.graphics.geo.Point

const val MONO_FILE_VERSION = 1
/**
* Version of the mono file
*
* # Version 0
* No mono file format. It was just a serialization of the root group
*
* # Version 1
* The first version of MonoFile.
* - root: root group content
* - extra:
* - name
* - offset
* - version: 1
* - modified_timestamp_millis: timestamp in millisecond (local time)
*
* # Version 2
* Include `connectors`
* - connectors: list of serialization of line connectors
*/
const val MONO_FILE_VERSION = 2

/**
* A data class for serializing shape to Json and load shape from Json.
*
*/
@Serializable
data class MonoFile internal constructor(
Expand All @@ -24,13 +42,20 @@ data class MonoFile internal constructor(
@SerialName("version")
val version: Int,
@SerialName("modified_timestamp_millis")
val modifiedTimestampMillis: Long
val modifiedTimestampMillis: Long,
@SerialName("connectors")
val connectors: List<SerializableLineConnector> = emptyList()
) {
constructor(root: SerializableGroup, extra: Extra) : this(
root,
extra,
MONO_FILE_VERSION,
currentTimeMillis().toLong()
constructor(
root: SerializableGroup,
connectors: List<SerializableLineConnector>,
extra: Extra
) : this(
root = root,
connectors = connectors,
extra = extra,
version = MONO_FILE_VERSION,
modifiedTimestampMillis = currentTimeMillis().toLong()
)
}

Expand Down
Loading

0 comments on commit 5e01d31

Please sign in to comment.