marksmith 0.0.11 → 0.0.12

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5eb26aeaa24188df02fdfaa2f3eccaecc28feac32420ff31e34328359b7939c8
4
- data.tar.gz: e5ae028f87a6c7bceeea0ac89269e320422d1466dd3b3de17d1c7dade9c49b0d
3
+ metadata.gz: 6223365f49536a9ee694cd03965e95e7ad4678f1baf8c2d5b8326b4e255f3c14
4
+ data.tar.gz: 300bf849b18a10fd9acedddfcfdff606e207d923d0aa9faa7f29a5e0a5703c46
5
5
  SHA512:
6
- metadata.gz: 578c3ed2d9ee8c3d58499f5c08edd8b469166f45f98c7b746912a95c04e780b5c310610ec6b83330253e924706827e9c6ae633fadd2f9e7347b2d1f084f0f017
7
- data.tar.gz: 9c4f92ce4997de77e065a9906994dbb4eb611f79648fa4acefcf61f83a02dded7c6a9b9203815f72dbedfb824ad38c9824f79004641133a1352554db5263d5e1
6
+ metadata.gz: 10a5f5e0597d1a1c4f6119e09919833d56878a94fb96dc6d50e3ca7ebc2396a8f3afdd073f9c06c2b066b185f9d22456d8f85835400b581177a440e3bc6af2cd
7
+ data.tar.gz: c94d8651053d3870af6ba50eabbf9d4147098a47b22e16b2341307d4074edc4118ee0c923cf58ee0eb5c326c9ce8f807d74d5ec92ce6d8f250c89df2ef572951
data/README.md CHANGED
@@ -50,9 +50,9 @@ npm install @avo-hq/marksmith
50
50
  Import and register it in your application.
51
51
 
52
52
  ```js
53
- import Marksmith from '@avo-hq/marksmith'
53
+ import { MarksmithController } from '@avo-hq/marksmith'
54
54
 
55
- application.register('marksmith', Marksmith)
55
+ application.register('marksmith', MarksmithController)
56
56
  ```
57
57
 
58
58
  > [!NOTE]
@@ -61,9 +61,9 @@ application.register('marksmith', Marksmith)
61
61
 
62
62
  ```js
63
63
  // Import just the controller
64
- import Marksmith from '@avo-hq/marksmith/controller'
64
+ import { MarksmithController } from '@avo-hq/marksmith/core'
65
65
 
66
- application.register('marksmith', Marksmith)
66
+ application.register('marksmith', MarksmithController)
67
67
 
68
68
  // Manually import Marksmith's dependencies
69
69
  import '@github/markdown-toolbar-element'
@@ -102,6 +102,20 @@ It supports basic styles for headings, `strong`, `italic` and others.
102
102
 
103
103
  The field supports Actve Storage uploads using drag and drop and pasting files into the field.
104
104
 
105
+ ## List continuation
106
+
107
+ Marksmith has this great opt-in feature where you can have your lists continued.
108
+ We need to add the `ListContinuation` controller too.
109
+
110
+ ```js
111
+ import { ListContinuationController, MarksmithController } from '@avo-hq/marksmith'
112
+ // or /core for the no-dependencies version
113
+ import { ListContinuationController, MarksmithController } from '@avo-hq/marksmith/core'
114
+
115
+ application.register('marksmith', MarksmithController)
116
+ application.register('list-continuation', ListContinuationController)
117
+ ```
118
+
105
119
  ## Contributing
106
120
 
107
121
  Contribution directions go here.
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="currentColor" width="800px" height="800px" viewBox="0 0 24 24" role="img"><script xmlns="" src="chrome-extension://hoklmmgfnpapgjgcpechhaamimifchmp/frame_ant/frame_ant.js"/><title>Markdown icon</title><path d="M22.269 19.385H1.731a1.73 1.73 0 0 1-1.73-1.73V6.345a1.73 1.73 0 0 1 1.73-1.73h20.538a1.73 1.73 0 0 1 1.73 1.73v11.308a1.73 1.73 0 0 1-1.73 1.731zm-16.5-3.462v-4.5l2.308 2.885 2.307-2.885v4.5h2.308V8.078h-2.308l-2.307 2.885-2.308-2.885H3.461v7.847zM21.231 12h-2.308V8.077h-2.307V12h-2.308l3.461 4.039z"/></svg>
@@ -0,0 +1,3 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="size-6">
2
+ <path stroke-linecap="round" stroke-linejoin="round" d="m18.375 12.739-7.693 7.693a4.5 4.5 0 0 1-6.364-6.364l10.94-10.94A3 3 0 1 1 19.5 7.372L8.552 18.32m.009-.01-.01.01m5.699-9.941-7.81 7.81a1.5 1.5 0 0 0 2.112 2.13" />
3
+ </svg>
@@ -1,4 +1,4 @@
1
- /*! tailwindcss v4.0.0-beta.10 | MIT License | https://tailwindcss.com */
1
+ /*! tailwindcss v4.0.0 | MIT License | https://tailwindcss.com */
2
2
  :root {
3
3
  --ms-font-sans: ui-sans-serif, system-ui, sans-serif, 'Apple Color Emoji', 'Segoe UI Emoji',
4
4
  'Segoe UI Symbol', 'Noto Color Emoji';
@@ -801,18 +801,31 @@
801
801
  width: calc(var(--ms-spacing) * 4);
802
802
  height: calc(var(--ms-spacing) * 4);
803
803
  }
804
+ .ms\:size-full {
805
+ width: 100%;
806
+ height: 100%;
807
+ }
804
808
  .ms\:w-full {
805
809
  width: 100%;
806
810
  }
811
+ .ms\:max-w-none {
812
+ max-width: none;
813
+ }
807
814
  .ms\:flex-1 {
808
815
  flex: 1;
809
816
  }
817
+ .ms\:flex-grow {
818
+ flex-grow: 1;
819
+ }
810
820
  .ms\:grow {
811
821
  flex-grow: 1;
812
822
  }
813
823
  .ms\:cursor-pointer {
814
824
  cursor: pointer;
815
825
  }
826
+ .ms\:resize-y {
827
+ resize: vertical;
828
+ }
816
829
  .ms\:flex-col {
817
830
  flex-direction: column;
818
831
  }
@@ -822,9 +835,22 @@
822
835
  .ms\:flex-wrap {
823
836
  flex-wrap: wrap;
824
837
  }
838
+ .ms\:items-center {
839
+ align-items: center;
840
+ }
841
+ .ms\:gap-x-2 {
842
+ column-gap: calc(var(--ms-spacing) * 2);
843
+ }
825
844
  .ms\:gap-y-1 {
826
845
  row-gap: calc(var(--ms-spacing) * 1);
827
846
  }
847
+ .ms\:space-x-2 {
848
+ :where(& > :not(:last-child)) {
849
+ --tw-space-x-reverse: 0;
850
+ margin-inline-start: calc(calc(var(--ms-spacing) * 2) * var(--tw-space-x-reverse));
851
+ margin-inline-end: calc(calc(var(--ms-spacing) * 2) * calc(1 - var(--tw-space-x-reverse)));
852
+ }
853
+ }
828
854
  .ms\:rounded {
829
855
  border-radius: 0.25rem;
830
856
  }
@@ -844,18 +870,27 @@
844
870
  --tw-border-style: none;
845
871
  border-style: none;
846
872
  }
847
- .ms\:border-zinc-300 {
848
- border-color: var(--ms-color-zinc-300);
873
+ .ms\:border-neutral-300 {
874
+ border-color: var(--ms-color-neutral-300);
875
+ }
876
+ .ms\:bg-neutral-50 {
877
+ background-color: var(--ms-color-neutral-50);
849
878
  }
850
- .ms\:bg-zinc-50 {
851
- background-color: var(--ms-color-zinc-50);
879
+ .ms\:bg-neutral-200 {
880
+ background-color: var(--ms-color-neutral-200);
852
881
  }
853
- .ms\:bg-zinc-200 {
854
- background-color: var(--ms-color-zinc-200);
882
+ .ms\:bg-transparent {
883
+ background-color: transparent;
855
884
  }
856
885
  .ms\:bg-none {
857
886
  background-image: none;
858
887
  }
888
+ .ms\:p-2 {
889
+ padding: calc(var(--ms-spacing) * 2);
890
+ }
891
+ .ms\:px-1 {
892
+ padding-inline: calc(var(--ms-spacing) * 1);
893
+ }
859
894
  .ms\:px-1\.5 {
860
895
  padding-inline: calc(var(--ms-spacing) * 1.5);
861
896
  }
@@ -871,56 +906,109 @@
871
906
  .ms\:py-2 {
872
907
  padding-block: calc(var(--ms-spacing) * 2);
873
908
  }
874
- .ms\:prose-zinc {
875
- --tw-prose-body: oklch(0.37 0.013 285.805);
876
- --tw-prose-headings: oklch(0.21 0.006 285.885);
877
- --tw-prose-lead: oklch(0.442 0.017 285.786);
878
- --tw-prose-links: oklch(0.21 0.006 285.885);
879
- --tw-prose-bold: oklch(0.21 0.006 285.885);
880
- --tw-prose-counters: oklch(0.552 0.016 285.938);
881
- --tw-prose-bullets: oklch(0.871 0.006 286.286);
882
- --tw-prose-hr: oklch(0.92 0.004 286.32);
883
- --tw-prose-quotes: oklch(0.21 0.006 285.885);
884
- --tw-prose-quote-borders: oklch(0.92 0.004 286.32);
885
- --tw-prose-captions: oklch(0.552 0.016 285.938);
886
- --tw-prose-kbd: oklch(0.21 0.006 285.885);
909
+ .ms\:py-px {
910
+ padding-block: 1px;
911
+ }
912
+ .ms\:font-mono {
913
+ font-family: var(--ms-font-mono);
914
+ }
915
+ .ms\:font-sans {
916
+ font-family: var(--ms-font-sans);
917
+ }
918
+ .ms\:text-sm {
919
+ font-size: var(--ms-text-sm);
920
+ line-height: var(--tw-leading, var(--ms-text-sm--line-height));
921
+ }
922
+ .ms\:text-xs {
923
+ font-size: var(--ms-text-xs);
924
+ line-height: var(--tw-leading, var(--ms-text-xs--line-height));
925
+ }
926
+ .ms\:font-semibold {
927
+ --tw-font-weight: var(--ms-font-weight-semibold);
928
+ font-weight: var(--ms-font-weight-semibold);
929
+ }
930
+ .ms\:text-neutral-600 {
931
+ color: var(--ms-color-neutral-600);
932
+ }
933
+ .ms\:text-neutral-800 {
934
+ color: var(--ms-color-neutral-800);
935
+ }
936
+ .ms\:uppercase {
937
+ text-transform: uppercase;
938
+ }
939
+ .ms\:no-underline {
940
+ text-decoration-line: none;
941
+ }
942
+ .ms\:prose-neutral {
943
+ --tw-prose-body: oklch(0.371 0 0);
944
+ --tw-prose-headings: oklch(0.205 0 0);
945
+ --tw-prose-lead: oklch(0.439 0 0);
946
+ --tw-prose-links: oklch(0.205 0 0);
947
+ --tw-prose-bold: oklch(0.205 0 0);
948
+ --tw-prose-counters: oklch(0.556 0 0);
949
+ --tw-prose-bullets: oklch(0.87 0 0);
950
+ --tw-prose-hr: oklch(0.922 0 0);
951
+ --tw-prose-quotes: oklch(0.205 0 0);
952
+ --tw-prose-quote-borders: oklch(0.922 0 0);
953
+ --tw-prose-captions: oklch(0.556 0 0);
954
+ --tw-prose-kbd: oklch(0.205 0 0);
887
955
  --tw-prose-kbd-shadows: NaN NaN NaN;
888
- --tw-prose-code: oklch(0.21 0.006 285.885);
889
- --tw-prose-pre-code: oklch(0.92 0.004 286.32);
890
- --tw-prose-pre-bg: oklch(0.274 0.006 286.033);
891
- --tw-prose-th-borders: oklch(0.871 0.006 286.286);
892
- --tw-prose-td-borders: oklch(0.92 0.004 286.32);
893
- --tw-prose-invert-body: oklch(0.871 0.006 286.286);
956
+ --tw-prose-code: oklch(0.205 0 0);
957
+ --tw-prose-pre-code: oklch(0.922 0 0);
958
+ --tw-prose-pre-bg: oklch(0.269 0 0);
959
+ --tw-prose-th-borders: oklch(0.87 0 0);
960
+ --tw-prose-td-borders: oklch(0.922 0 0);
961
+ --tw-prose-invert-body: oklch(0.87 0 0);
894
962
  --tw-prose-invert-headings: #fff;
895
- --tw-prose-invert-lead: oklch(0.705 0.015 286.067);
963
+ --tw-prose-invert-lead: oklch(0.708 0 0);
896
964
  --tw-prose-invert-links: #fff;
897
965
  --tw-prose-invert-bold: #fff;
898
- --tw-prose-invert-counters: oklch(0.705 0.015 286.067);
899
- --tw-prose-invert-bullets: oklch(0.442 0.017 285.786);
900
- --tw-prose-invert-hr: oklch(0.37 0.013 285.805);
901
- --tw-prose-invert-quotes: oklch(0.967 0.001 286.375);
902
- --tw-prose-invert-quote-borders: oklch(0.37 0.013 285.805);
903
- --tw-prose-invert-captions: oklch(0.705 0.015 286.067);
966
+ --tw-prose-invert-counters: oklch(0.708 0 0);
967
+ --tw-prose-invert-bullets: oklch(0.439 0 0);
968
+ --tw-prose-invert-hr: oklch(0.371 0 0);
969
+ --tw-prose-invert-quotes: oklch(0.97 0 0);
970
+ --tw-prose-invert-quote-borders: oklch(0.371 0 0);
971
+ --tw-prose-invert-captions: oklch(0.708 0 0);
904
972
  --tw-prose-invert-kbd: #fff;
905
973
  --tw-prose-invert-kbd-shadows: 255 255 255;
906
974
  --tw-prose-invert-code: #fff;
907
- --tw-prose-invert-pre-code: oklch(0.871 0.006 286.286);
975
+ --tw-prose-invert-pre-code: oklch(0.87 0 0);
908
976
  --tw-prose-invert-pre-bg: rgb(0 0 0 / 50%);
909
- --tw-prose-invert-th-borders: oklch(0.442 0.017 285.786);
910
- --tw-prose-invert-td-borders: oklch(0.37 0.013 285.805);
977
+ --tw-prose-invert-th-borders: oklch(0.439 0 0);
978
+ --tw-prose-invert-td-borders: oklch(0.371 0 0);
911
979
  }
912
- .ms\:focus-within\:border-zinc-500 {
980
+ .ms\:focus-within\:border-neutral-400 {
913
981
  &:focus-within {
914
- border-color: var(--ms-color-zinc-500);
982
+ border-color: var(--ms-color-neutral-400);
915
983
  }
916
984
  }
917
- .ms\:hover\:bg-zinc-200 {
985
+ .ms\:hover\:bg-neutral-100 {
918
986
  &:hover {
919
987
  @media (hover: hover) {
920
- background-color: var(--ms-color-zinc-200);
988
+ background-color: var(--ms-color-neutral-100);
921
989
  }
922
990
  }
923
991
  }
992
+ .ms\:hover\:bg-neutral-200 {
993
+ &:hover {
994
+ @media (hover: hover) {
995
+ background-color: var(--ms-color-neutral-200);
996
+ }
997
+ }
998
+ }
999
+ .ms\:hover\:bg-neutral-300 {
1000
+ &:hover {
1001
+ @media (hover: hover) {
1002
+ background-color: var(--ms-color-neutral-300);
1003
+ }
1004
+ }
1005
+ }
1006
+ .ms\:focus\:ring-0 {
1007
+ &:focus {
1008
+ --tw-ring-shadow: var(--tw-ring-inset,) 0 0 0 calc(0px + var(--tw-ring-offset-width)) var(--tw-ring-color, currentColor);
1009
+ box-shadow: var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow);
1010
+ }
1011
+ }
924
1012
  .ms\:focus\:outline-none {
925
1013
  &:focus {
926
1014
  --tw-outline-style: none;
@@ -932,6 +1020,45 @@
932
1020
  flex-direction: row;
933
1021
  }
934
1022
  }
1023
+ .ms\:button-spinner {
1024
+ width: 24px;
1025
+ height: 24px;
1026
+ position: relative;
1027
+ }
1028
+ .ms\:button-spinner > .double-bounce1, .ms\:button-spinner > .double-bounce2 {
1029
+ width: 100%;
1030
+ height: 100%;
1031
+ border-radius: 50%;
1032
+ background-color: #333;
1033
+ opacity: 0.5;
1034
+ position: absolute;
1035
+ top: 0;
1036
+ left: 0;
1037
+ -webkit-animation: sk-bounce 2s infinite ease-in-out;
1038
+ animation: sk-bounce 2s infinite ease-in-out;
1039
+ }
1040
+ .ms\:button-spinner > .double-bounce2 {
1041
+ -webkit-animation-delay: -1s;
1042
+ animation-delay: -1s;
1043
+ }
1044
+ @-webkit-keyframes sk-bounce {
1045
+ 0%, 100% {
1046
+ -webkit-transform: scale(0);
1047
+ }
1048
+ 50% {
1049
+ -webkit-transform: scale(1);
1050
+ }
1051
+ }
1052
+ @keyframes sk-bounce {
1053
+ 0%, 100% {
1054
+ transform: scale(0);
1055
+ -webkit-transform: scale(0);
1056
+ }
1057
+ 50% {
1058
+ transform: scale(1);
1059
+ -webkit-transform: scale(1);
1060
+ }
1061
+ }
935
1062
  @keyframes spin {
936
1063
  to {
937
1064
  transform: rotate(360deg);
@@ -958,8 +1085,72 @@
958
1085
  animation-timing-function: cubic-bezier(0, 0, 0.2, 1);
959
1086
  }
960
1087
  }
1088
+ @property --tw-space-x-reverse {
1089
+ syntax: "*";
1090
+ inherits: false;
1091
+ initial-value: 0;
1092
+ }
961
1093
  @property --tw-border-style {
962
1094
  syntax: "*";
963
1095
  inherits: false;
964
1096
  initial-value: solid;
965
1097
  }
1098
+ @property --tw-font-weight {
1099
+ syntax: "*";
1100
+ inherits: false;
1101
+ }
1102
+ @property --tw-shadow {
1103
+ syntax: "*";
1104
+ inherits: false;
1105
+ initial-value: 0 0 #0000;
1106
+ }
1107
+ @property --tw-shadow-color {
1108
+ syntax: "*";
1109
+ inherits: false;
1110
+ }
1111
+ @property --tw-inset-shadow {
1112
+ syntax: "*";
1113
+ inherits: false;
1114
+ initial-value: 0 0 #0000;
1115
+ }
1116
+ @property --tw-inset-shadow-color {
1117
+ syntax: "*";
1118
+ inherits: false;
1119
+ }
1120
+ @property --tw-ring-color {
1121
+ syntax: "*";
1122
+ inherits: false;
1123
+ }
1124
+ @property --tw-ring-shadow {
1125
+ syntax: "*";
1126
+ inherits: false;
1127
+ initial-value: 0 0 #0000;
1128
+ }
1129
+ @property --tw-inset-ring-color {
1130
+ syntax: "*";
1131
+ inherits: false;
1132
+ }
1133
+ @property --tw-inset-ring-shadow {
1134
+ syntax: "*";
1135
+ inherits: false;
1136
+ initial-value: 0 0 #0000;
1137
+ }
1138
+ @property --tw-ring-inset {
1139
+ syntax: "*";
1140
+ inherits: false;
1141
+ }
1142
+ @property --tw-ring-offset-width {
1143
+ syntax: "<length>";
1144
+ inherits: false;
1145
+ initial-value: 0px;
1146
+ }
1147
+ @property --tw-ring-offset-color {
1148
+ syntax: "*";
1149
+ inherits: false;
1150
+ initial-value: #fff;
1151
+ }
1152
+ @property --tw-ring-offset-shadow {
1153
+ syntax: "*";
1154
+ inherits: false;
1155
+ initial-value: 0 0 #0000;
1156
+ }
@@ -9,3 +9,54 @@
9
9
  @plugin "@tailwindcss/typography";
10
10
 
11
11
  @source "./../../views/";
12
+ @source "./../../helpers/";
13
+ @source "./../../../lib/";
14
+
15
+ .ms\:button-spinner {
16
+ width: 24px;
17
+ height: 24px;
18
+
19
+ position: relative;
20
+ }
21
+
22
+ .ms\:button-spinner > .double-bounce1,
23
+ .ms\:button-spinner > .double-bounce2 {
24
+ width: 100%;
25
+ height: 100%;
26
+ border-radius: 50%;
27
+ background-color: #333;
28
+ opacity: 0.5;
29
+ position: absolute;
30
+ top: 0;
31
+ left: 0;
32
+
33
+ -webkit-animation: sk-bounce 2s infinite ease-in-out;
34
+ animation: sk-bounce 2s infinite ease-in-out;
35
+ }
36
+
37
+ .ms\:button-spinner > .double-bounce2 {
38
+ -webkit-animation-delay: -1s;
39
+ animation-delay: -1s;
40
+ }
41
+
42
+ @-webkit-keyframes sk-bounce {
43
+ 0%,
44
+ 100% {
45
+ -webkit-transform: scale(0);
46
+ }
47
+ 50% {
48
+ -webkit-transform: scale(1);
49
+ }
50
+ }
51
+
52
+ @keyframes sk-bounce {
53
+ 0%,
54
+ 100% {
55
+ transform: scale(0);
56
+ -webkit-transform: scale(0);
57
+ }
58
+ 50% {
59
+ transform: scale(1);
60
+ -webkit-transform: scale(1);
61
+ }
62
+ }
@@ -0,0 +1,6 @@
1
+ import MarksmithController from "./marksmith_controller"
2
+ import ListContinuationController from "./list_continuation_controller"
3
+
4
+ export { MarksmithController, ListContinuationController }
5
+
6
+
@@ -0,0 +1,94 @@
1
+ import { Controller } from '@hotwired/stimulus'
2
+
3
+ export default class extends Controller {
4
+ connect() {
5
+ this.isInsertLineBreak = false
6
+ this.isProcessing = false // Guard flag to prevent recursion
7
+
8
+ this.SPACE_PATTERN = /^(\s*)?/
9
+ this.LIST_PATTERN = /^(\s*)([*-]|(\d+)\.)\s(\[[\sx]\]\s)?/
10
+ }
11
+
12
+ handleBeforeInput(event) {
13
+ if (this.isProcessing) return
14
+ this.isInsertLineBreak = event.inputType === 'insertLineBreak'
15
+ }
16
+
17
+ handleInput(event) {
18
+ if (this.isProcessing) return
19
+ if (this.isInsertLineBreak || event.inputType === 'insertLineBreak') {
20
+ this.handleListContinuation(event.target)
21
+ this.isInsertLineBreak = false
22
+ }
23
+ }
24
+
25
+ handleListContinuation(textarea) {
26
+ if (this.isProcessing) return
27
+
28
+ const result = this.analyzeCurrentLine(
29
+ textarea.value,
30
+ [textarea.selectionStart, textarea.selectionEnd],
31
+ )
32
+
33
+ if (result !== undefined) {
34
+ this.isProcessing = true
35
+ try {
36
+ this.applyTextChange(textarea, result)
37
+ } finally {
38
+ // Ensure we always reset the processing flag
39
+ setTimeout(() => {
40
+ this.isProcessing = false
41
+ }, 0)
42
+ }
43
+ }
44
+ }
45
+
46
+ analyzeCurrentLine(text, [cursorPosition]) {
47
+ if (!cursorPosition || !text) return
48
+
49
+ // Get all lines up to cursor
50
+ const lines = text.substring(0, cursorPosition).split('\n')
51
+ const previousLine = lines[lines.length - 2]
52
+
53
+ // If no previous line or doesn't match list pattern, do nothing
54
+ const match = previousLine?.match(this.LIST_PATTERN)
55
+ if (!match) return
56
+
57
+ const [fullMatch, indentation, listMarker, number, checkbox] = match
58
+
59
+ // Check if previous line was empty (just list marker)
60
+ const previousContent = previousLine.replace(fullMatch, '').trim()
61
+ if (previousContent.length === 0) {
62
+ // Terminate the list by removing the marker
63
+ const start = cursorPosition - `\n${fullMatch}`.length
64
+
65
+ return {
66
+ text: text.substring(0, start) + text.substring(cursorPosition),
67
+ selection: [start, start],
68
+ operation: 'delete',
69
+ }
70
+ }
71
+
72
+ // For numbered lists, increment the number
73
+ const newMarker = number ? `${parseInt(number, 10) + 1}.` : listMarker
74
+
75
+ // Maintain checkbox if it was present
76
+ const prefix = `${indentation}${newMarker} ${checkbox ? '[ ] ' : ''}`
77
+
78
+ // Continue the list with the same indentation and style
79
+ return {
80
+ text: text.substring(0, cursorPosition) + prefix + text.substring(cursorPosition),
81
+ selection: [cursorPosition + prefix.length, cursorPosition + prefix.length],
82
+ operation: 'insert',
83
+ }
84
+ }
85
+
86
+ applyTextChange(textarea, { text, selection }) {
87
+ // Set new value directly
88
+ textarea.value = text
89
+ // Set the cursor position
90
+ const [start, end] = selection
91
+ textarea.selectionStart = start
92
+ textarea.selectionEnd = end
93
+ }
94
+ }
@@ -17,10 +17,10 @@ export default class extends Controller {
17
17
  fieldId: String,
18
18
  }
19
19
 
20
- static targets = ['fieldElement', 'previewElement', 'writeTabButton', 'previewTabButton', 'toolbar']
20
+ static targets = ['fieldContainer', 'fieldElement', 'previewElement', 'writeTabButton', 'previewTabButton', 'toolbar']
21
21
 
22
22
  connect() {
23
- subscribe(this.fieldElementTarget, { defaultPlainTextPaste: { urlLinks: true } })
23
+ subscribe(this.fieldContainerTarget, { defaultPlainTextPaste: { urlLinks: true } })
24
24
  }
25
25
 
26
26
  switchToWrite(event) {
@@ -31,7 +31,7 @@ export default class extends Controller {
31
31
  this.previewTabButtonTarget.classList.remove('ms:hidden')
32
32
 
33
33
  // toggle write/preview buttons
34
- this.fieldElementTarget.classList.remove('ms:hidden')
34
+ this.fieldContainerTarget.classList.remove('ms:hidden')
35
35
  this.previewElementTarget.classList.add('ms:hidden')
36
36
 
37
37
  // toggle the toolbar back
@@ -58,7 +58,7 @@ export default class extends Controller {
58
58
  this.previewTabButtonTarget.classList.add('ms:hidden')
59
59
 
60
60
  // toggle elements
61
- this.fieldElementTarget.classList.add('ms:hidden')
61
+ this.fieldContainerTarget.classList.add('ms:hidden')
62
62
  this.previewElementTarget.classList.remove('ms:hidden')
63
63
 
64
64
  // toggle the toolbar
@@ -77,6 +77,21 @@ export default class extends Controller {
77
77
  this.uploadFiles(event.clipboardData.files)
78
78
  }
79
79
 
80
+ buttonUpload(event) {
81
+ event.preventDefault()
82
+ // Create a hidden file input and trigger it
83
+ const fileInput = document.createElement('input')
84
+ fileInput.type = 'file'
85
+ fileInput.multiple = true
86
+ fileInput.accept = 'image/*,.pdf,.doc,.docx,.txt'
87
+
88
+ fileInput.addEventListener('change', (e) => {
89
+ this.uploadFiles(e.target.files)
90
+ })
91
+
92
+ fileInput.click()
93
+ }
94
+
80
95
  uploadFiles(files) {
81
96
  Array.from(files).forEach((file) => this.uploadFile(file))
82
97
  }
@@ -6,6 +6,7 @@
6
6
  style = local_assigns[:style] || nil
7
7
  classes = local_assigns[:class] || nil
8
8
  rows = local_assigns[:rows] || 15
9
+ field_name = form&.field_name(name) || name
9
10
  value = if defined?(form)
10
11
  form.object.send(name)
11
12
  else
@@ -14,21 +15,25 @@
14
15
  extra_preview_params = local_assigns[:extra_preview_params] || {}
15
16
  %>
16
17
  <%= content_tag :div,
17
- class: "ms:flex ms:flex-col ms:w-full ms:border ms:border-zinc-300 ms:rounded ms:@container ms:group ms:focus-within:border-zinc-500",
18
+ class: "ms:flex ms:flex-col ms:w-full ms:border ms:border-neutral-300 ms:rounded ms:@container ms:group ms:focus-within:border-neutral-400",
18
19
  data: {
19
- controller: "marksmith",
20
+ controller: "marksmith list-continuation",
21
+ action: "
22
+ beforeinput->list-continuation#handleBeforeInput
23
+ input->list-continuation#handleInput
24
+ ",
20
25
  marksmith_preview_url_value: marksmith.markdown_previews_path,
21
26
  marksmith_active_tab_class: "bg-white",
22
27
  marksmith_attach_url_value: main_app.rails_direct_uploads_url,
23
28
  marksmith_extra_preview_params_value: extra_preview_params.as_json,
24
29
  } do %>
25
- <% toggle_button_classes = class_names(marksmith_button_classes, "ms:border-0 ms:bg-none") %>
26
- <div class="ms:flex-1 ms:flex-col-reverse ms:@md:flex-row ms:grow ms:flex ms:justify-bewteen ms:bg-zinc-50 ms:rounded ms:px-2 ms:py-1 ms:gap-y-1">
30
+ <% toggle_button_classes = class_names(marksmith_button_classes, "ms:bg-neutral-200 ms:border-0 ms:bg-none ms:text-sm ms:hover:bg-neutral-300 ms:uppercase ms:text-xs ms:font-semibold ms:text-neutral-800") %>
31
+ <div class="ms:flex-1 ms:flex-col-reverse ms:@md:flex-row ms:grow ms:flex ms:justify-bewteen ms:bg-neutral-50 ms:rounded ms:px-2 ms:py-1 ms:gap-y-1">
27
32
  <div class="ms:flex-1 ms:flex ms:items:center">
28
- <button class="<%= toggle_button_classes %>" data-action="click->marksmith#switchToPreview" data-marksmith-target="previewTabButton">
33
+ <button class="<%= toggle_button_classes %>" data-action="click->marksmith#switchToPreview" data-marksmith-target="previewTabButton" type="button">
29
34
  <%= t('marksmith.preview').humanize %>
30
35
  </button>
31
- <button class="<%= toggle_button_classes %> ms:hidden ms:bg-zinc-200" data-action="click->marksmith#switchToWrite" data-marksmith-target="writeTabButton">
36
+ <button class="<%= toggle_button_classes %> ms:hidden ms:bg-neutral-200" data-action="click->marksmith#switchToWrite" data-marksmith-target="writeTabButton" type="button">
32
37
  <%= t('marksmith.write').humanize %>
33
38
  </button>
34
39
  </div>
@@ -46,29 +51,39 @@
46
51
  <%= marksmith_toolbar_button "task-list" %>
47
52
  </markdown-toolbar>
48
53
  </div>
49
-
50
- <div class="ms:border-t ms:border-zinc-300 ms:flex">
51
- <%= text_area_tag name, value,
52
- id: name,
53
- class: class_names("ms:flex ms:flex-1 ms:rounded ms:border-none ms:py-2 ms:px-3 ms:focus:outline-none", classes),
54
- rows: rows,
55
- data: {
56
- marksmith_target: "fieldElement",
57
- action: "drop->marksmith#dropUpload paste->marksmith#pasteUpload",
58
- **data_attributes
59
- },
60
- disabled:,
61
- placeholder:,
62
- autofocus:,
63
- style:
64
- %>
54
+ <% toolbar_button_classes = "ms:cursor-pointer ms:hover:bg-neutral-100 ms:px-1 ms:py-px ms:rounded ms:text-sm" %>
55
+ <div class="ms:border-t ms:border-neutral-300 ms:flex">
56
+ <%= content_tag :div, class: "ms:flex ms:flex-col ms:size-full", data: { marksmith_target: "fieldContainer" } do %>
57
+ <%= text_area_tag field_name, value,
58
+ id: name,
59
+ class: class_names("ms:flex ms:flex-1 ms:rounded ms:border-none ms:p-2 ms:resize-y ms:focus:outline-none ms:font-mono ms:focus:ring-0", classes),
60
+ rows: rows,
61
+ data: {
62
+ action: "drop->marksmith#dropUpload paste->marksmith#pasteUpload",
63
+ marksmith_target: "fieldElement",
64
+ **data_attributes
65
+ },
66
+ disabled:,
67
+ placeholder:,
68
+ autofocus:,
69
+ style:
70
+ %>
71
+ <div class="ms:flex ms:w-full ms:flex-1 ms:flex-grow ms:space-x-2 ms:py-1 ms:border-t ms:border-neutral-300 ms:px-2 ms:font-sans ms:text-sm">
72
+ <%= link_to "https://docs.github.com/github/writing-on-github/getting-started-with-writing-and-formatting-on-github/basic-writing-and-formatting-syntax", target: "_blank", class: class_names("ms:flex ms:items-center ms:gap-x-2 ms:text-neutral-800 ms:no-underline", toolbar_button_classes) do %>
73
+ <%= image_tag asset_path("marksmith/svgs/markdown.svg"), class: "ms:inline ms:size-4" %> <%= t("marksmith.markdown_is_supported").humanize %>
74
+ <% end %>
75
+ <%= button_tag data: { action: "click->marksmith#buttonUpload" }, class: class_names("ms:bg-none ms:border-none ms:bg-transparent ms:text-neutral-600 ms:items-center ms:flex", toolbar_button_classes) do %>
76
+ <%= image_tag asset_path("marksmith/svgs/paperclip.svg"), class: "ms:inline ms:size-4" %> <%= t("marksmith.attach_files").humanize %>
77
+ <% end %>
78
+ </div>
79
+ <% end %>
65
80
  <%= content_tag :div,
66
- class: "ms:hidden ms:markdown-preview",
81
+ class: "ms:hidden ms:markdown-preview ms:size-full ms:flex-1 ms:flex ms:size-full",
67
82
  id: "markdown-preview-#{name}",
68
83
  data: {
69
84
  marksmith_target: "previewElement",
70
85
  } do %>
71
- <div class="button-spinner">
86
+ <div class="ms:button-spinner">
72
87
  <div class="double-bounce1"></div>
73
88
  <div class="double-bounce2"></div>
74
89
  </div>
@@ -1,3 +1,3 @@
1
- <%= content_tag :div, class: "ms:prose ms:prose-zinc" do %>
1
+ <%= content_tag :div, class: "ms:prose ms:prose-neutral ms:max-w-none" do %>
2
2
  <%= sanitize(body, tags: %w(table th tr td span) + ActionView::Helpers::SanitizeHelper.sanitizer_vendor.safe_list_sanitizer.allowed_tags.to_a) %>
3
3
  <% end %>
@@ -30,12 +30,14 @@
30
30
 
31
31
  en:
32
32
  marksmith:
33
+ attach_files: attach files
33
34
  bold: bold
34
35
  code: code
35
36
  header: header
36
37
  image: image
37
38
  italic: italic
38
39
  link: link
40
+ markdown_is_supported: Markdown is supported
39
41
  ordered_list: ordered list
40
42
  preview: preview
41
43
  quote: quote
@@ -14,7 +14,7 @@ module Marksmith
14
14
  end
15
15
 
16
16
  def marksmith_button_classes
17
- class_names("ms:flex ms:items:center ms:cursor-pointer ms:py-1 ms:px-1.5 ms:hover:bg-zinc-200 ms:rounded")
17
+ class_names("ms:flex ms:items:center ms:cursor-pointer ms:py-1 ms:px-1.5 ms:hover:bg-neutral-200 ms:rounded")
18
18
  end
19
19
 
20
20
  def marksmith_toolbar_button(name, **kwargs)
@@ -1,3 +1,3 @@
1
1
  module Marksmith
2
- VERSION = "0.0.11"
2
+ VERSION = "0.0.12"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: marksmith
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.11
4
+ version: 0.0.12
5
5
  platform: ruby
6
6
  authors:
7
7
  - Adrian Marin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2025-01-22 00:00:00.000000000 Z
11
+ date: 2025-01-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -58,7 +58,9 @@ files:
58
58
  - app/assets/images/marksmith/svgs/italic.svg
59
59
  - app/assets/images/marksmith/svgs/link copy.svg
60
60
  - app/assets/images/marksmith/svgs/link.svg
61
+ - app/assets/images/marksmith/svgs/markdown.svg
61
62
  - app/assets/images/marksmith/svgs/ordered-list.svg
63
+ - app/assets/images/marksmith/svgs/paperclip.svg
62
64
  - app/assets/images/marksmith/svgs/quote.svg
63
65
  - app/assets/images/marksmith/svgs/task-list.svg
64
66
  - app/assets/images/marksmith/svgs/unordered-list.svg
@@ -69,7 +71,9 @@ files:
69
71
  - app/frontend/entrypoints/application.css
70
72
  - app/frontend/entrypoints/application.js
71
73
  - app/frontend/entrypoints/javascript/controllers/application.js
74
+ - app/frontend/entrypoints/javascript/controllers/export.js
72
75
  - app/frontend/entrypoints/javascript/controllers/index.js
76
+ - app/frontend/entrypoints/javascript/controllers/list_continuation_controller.js
73
77
  - app/frontend/entrypoints/javascript/controllers/marksmith_controller.js
74
78
  - app/helpers/marksmith/application_helper.rb
75
79
  - app/jobs/marksmith/application_job.rb