smeditor 0.2.4 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CHANGELOG.md +12 -0
- data/README.md +3 -0
- data/app/assets/javascripts/smeditor.js +46 -24
- data/app/assets/stylesheets/smeditor.css +7 -36
- data/lib/generators/smeditor/templates/initializer.rb +3 -0
- data/lib/smeditor/rails/config.rb +3 -0
- data/lib/smeditor/rails/form_helper.rb +26 -10
- data/lib/smeditor/rails/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 37d69366d661f31b412b1225d656367136e26de3ac8ad98988ceeaf251ddc942
|
|
4
|
+
data.tar.gz: fbc0c5332adb77f6a42b1716c403a31e3dd21d8eb79919ee214073a69caab8ed
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5db1e9fa62c9bf728776931b9276184d18b85c36e586efbe907fae818cd6ecf4785d8144e66b922a3e567f64db97a546d96ca3ee4ecd5b8aa3c655b7933bd496
|
|
7
|
+
data.tar.gz: 9426a009a1184a403397818a2f37600702dbf72c3ecaa73bd7c0ffff44ab0f638416b033cc8e3208bbce664fba1fca97182bb7410b61e5b69dcc9bbff4f4f4ec
|
data/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog — smeditor
|
|
2
2
|
|
|
3
|
+
## 0.3.0 — 2026-09-13
|
|
4
|
+
|
|
5
|
+
- Added explicit `light` and `dark` theme selection across Core, React, and the Rails adapter. Rails now also supports a global `config.default_theme` and per-editor `theme` option.
|
|
6
|
+
- The editor now defaults to the light theme when no theme is configured; dark mode is enabled only when explicitly requested, instead of following `prefers-color-scheme`.
|
|
7
|
+
- Fixed React `EditorContent` overriding an editor created with `editable: false` back to an editable surface when the `editable` prop is omitted.
|
|
8
|
+
|
|
9
|
+
## 0.2.5 — 2026-09-09
|
|
10
|
+
|
|
11
|
+
- Fixed the Rails selected-text bubble toolbar being displaced to the right or vertically aligned with the text when the host application creates a CSS containing block (for example with `transform`, `filter`, `perspective`, or modal/animated wrappers).
|
|
12
|
+
- Bubble positioning now matches the playground: it is centered above the live selection, flips below near the top viewport edge, and clamps horizontally to the viewport.
|
|
13
|
+
- The Rails bubble is positioned in editor-local coordinates, so host layout transforms no longer corrupt its placement.
|
|
14
|
+
|
|
3
15
|
## 0.2.4 — 2026-09-09
|
|
4
16
|
|
|
5
17
|
- Added the selected-text bubble formatting toolbar to the self-contained Rails adapter, matching the playground behavior.
|
data/README.md
CHANGED
|
@@ -42,6 +42,7 @@ SMEditor.configure do |config|
|
|
|
42
42
|
config.uploads = :none # or :active_storage
|
|
43
43
|
config.upload_path = "/smeditor/uploads"
|
|
44
44
|
config.default_kit = "starter" # or "full"
|
|
45
|
+
config.default_theme = "light" # or "dark"
|
|
45
46
|
config.min_height = 320 # desktop, px when numeric
|
|
46
47
|
config.tablet_min_height = 280 # <= 1024px
|
|
47
48
|
config.mobile_min_height = 220 # <= 640px
|
|
@@ -64,6 +65,7 @@ end
|
|
|
64
65
|
<%= form_with model: @article do |form| %>
|
|
65
66
|
<%= form.smeditor_editor :content,
|
|
66
67
|
kit: "full",
|
|
68
|
+
theme: "light",
|
|
67
69
|
placeholder: "Write…",
|
|
68
70
|
min_height: 420,
|
|
69
71
|
tablet_min_height: 340,
|
|
@@ -81,6 +83,7 @@ observe the value without CKEditor-specific integration.
|
|
|
81
83
|
Available options:
|
|
82
84
|
|
|
83
85
|
- `kit: "starter" | "full"`
|
|
86
|
+
- `theme: "light" | "dark"` — defaults to `config.default_theme` (`"light"`)
|
|
84
87
|
- `placeholder:`
|
|
85
88
|
- `class:`
|
|
86
89
|
- `label:` — editor ARIA label
|
|
@@ -463,13 +463,18 @@ function selectionForSurface(surface) {
|
|
|
463
463
|
* This mirrors React's <BubbleMenu />: selecting non-empty text opens a small
|
|
464
464
|
* formatting toolbar above the live DOM range.
|
|
465
465
|
*/
|
|
466
|
-
function buildBubbleMenu(editor, surface) {
|
|
466
|
+
function buildBubbleMenu(editor, surface, container) {
|
|
467
467
|
var _a;
|
|
468
468
|
const menu = document.createElement("div");
|
|
469
469
|
menu.className = "smeditor-floating smeditor-bubble-menu";
|
|
470
470
|
menu.setAttribute("role", "toolbar");
|
|
471
471
|
menu.setAttribute("aria-label", "Selected text formatting");
|
|
472
|
-
|
|
472
|
+
// Keep the popover in the editor's own coordinate space. `position: fixed`
|
|
473
|
+
// becomes relative to transformed/filtering ancestors in browsers, which can
|
|
474
|
+
// shove the menu to the right or down in host applications. Absolute local
|
|
475
|
+
// positioning is stable because both the selection and container are measured
|
|
476
|
+
// in viewport coordinates and then converted to editor-local coordinates.
|
|
477
|
+
menu.style.position = "absolute";
|
|
473
478
|
menu.style.display = "none";
|
|
474
479
|
menu.style.zIndex = "2147483000";
|
|
475
480
|
const buttons = [
|
|
@@ -515,20 +520,27 @@ function buildBubbleMenu(editor, surface) {
|
|
|
515
520
|
menu.style.left = "-9999px";
|
|
516
521
|
menu.style.top = "-9999px";
|
|
517
522
|
const rect = menu.getBoundingClientRect();
|
|
523
|
+
const containerRect = container.getBoundingClientRect();
|
|
518
524
|
const margin = 8;
|
|
519
525
|
const offset = 8;
|
|
520
526
|
const viewportWidth = window.innerWidth;
|
|
521
527
|
const viewportHeight = window.innerHeight;
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
528
|
+
// Calculate the desired position in viewport coordinates first, exactly like
|
|
529
|
+
// the React playground's Floating primitive, including viewport clamping and
|
|
530
|
+
// top/bottom collision handling. Then translate that point into the local
|
|
531
|
+
// coordinate system of the editor shell. This survives transformed parents,
|
|
532
|
+
// zoomed panels, modals, sidebars and other containing-block creators.
|
|
533
|
+
let viewportLeft = anchor.left + anchor.width / 2 - rect.width / 2;
|
|
534
|
+
const maxViewportLeft = Math.max(margin, viewportWidth - rect.width - margin);
|
|
535
|
+
viewportLeft = Math.max(margin, Math.min(viewportLeft, maxViewportLeft));
|
|
536
|
+
let viewportTop = anchor.top - rect.height - offset;
|
|
537
|
+
if (viewportTop < margin)
|
|
538
|
+
viewportTop = anchor.bottom + offset;
|
|
539
|
+
if (viewportTop + rect.height > viewportHeight - margin) {
|
|
540
|
+
viewportTop = Math.max(margin, anchor.top - rect.height - offset);
|
|
541
|
+
}
|
|
542
|
+
menu.style.left = `${Math.round(viewportLeft - containerRect.left)}px`;
|
|
543
|
+
menu.style.top = `${Math.round(viewportTop - containerRect.top)}px`;
|
|
532
544
|
menu.style.visibility = "visible";
|
|
533
545
|
};
|
|
534
546
|
const scheduleUpdate = () => {
|
|
@@ -583,10 +595,15 @@ function bootMount(mount) {
|
|
|
583
595
|
const kit = mount.dataset.smeditorKit === "full" ? "full" : "starter";
|
|
584
596
|
const uploadUrl = mount.dataset.smeditorUploadUrl || undefined;
|
|
585
597
|
const placeholder = mount.dataset.smeditorPlaceholder || undefined;
|
|
586
|
-
const
|
|
598
|
+
const configuredTheme = mount.dataset.smeditorTheme;
|
|
599
|
+
const inheritedTheme = ((_b = (_a = mount.parentElement) === null || _a === void 0 ? void 0 : _a.closest("[data-theme]")) === null || _b === void 0 ? void 0 : _b.dataset.theme)
|
|
587
600
|
|| document.documentElement.dataset.theme;
|
|
588
|
-
|
|
589
|
-
|
|
601
|
+
const theme = configuredTheme === "dark" || configuredTheme === "light"
|
|
602
|
+
? configuredTheme
|
|
603
|
+
: inheritedTheme === "dark" || inheritedTheme === "light"
|
|
604
|
+
? inheritedTheme
|
|
605
|
+
: "light";
|
|
606
|
+
mount.dataset.theme = theme;
|
|
590
607
|
const root = shadowRootFor(mount);
|
|
591
608
|
const shell = document.createElement("div");
|
|
592
609
|
shell.className = "smeditor";
|
|
@@ -609,6 +626,7 @@ function bootMount(mount) {
|
|
|
609
626
|
extensions: kitExtensions(kit, uploadUrl),
|
|
610
627
|
content: input.value || "<p></p>",
|
|
611
628
|
placeholder,
|
|
629
|
+
theme,
|
|
612
630
|
deepSelection: true,
|
|
613
631
|
onUpdate: ({ editor: current }) => {
|
|
614
632
|
input.value = current.getHTML();
|
|
@@ -618,8 +636,8 @@ function bootMount(mount) {
|
|
|
618
636
|
});
|
|
619
637
|
const toolbar = buildToolbar(editor, kit, uploadUrl);
|
|
620
638
|
shell.insertBefore(toolbar, surface);
|
|
621
|
-
const bubbleMenu = buildBubbleMenu(editor, surface);
|
|
622
|
-
|
|
639
|
+
const bubbleMenu = buildBubbleMenu(editor, surface, shell);
|
|
640
|
+
shell.appendChild(bubbleMenu);
|
|
623
641
|
input.smeditorInstance = editor;
|
|
624
642
|
mount.smeditorInstance = editor;
|
|
625
643
|
mount.dataset.smeditorBooted = "1";
|
|
@@ -788,7 +806,7 @@ function createEditor(options) {
|
|
|
788
806
|
}
|
|
789
807
|
class Editor {
|
|
790
808
|
constructor(options) {
|
|
791
|
-
var _a, _b, _c;
|
|
809
|
+
var _a, _b, _c, _d;
|
|
792
810
|
this.selection = null;
|
|
793
811
|
this.history = new history_js_1.History();
|
|
794
812
|
this.emitter = new events_js_1.EventEmitter();
|
|
@@ -818,10 +836,13 @@ class Editor {
|
|
|
818
836
|
// Capture the finished composed text in one shot.
|
|
819
837
|
this.handleDOMInput();
|
|
820
838
|
};
|
|
821
|
-
this.options =
|
|
822
|
-
|
|
839
|
+
this.options = {
|
|
840
|
+
...options,
|
|
841
|
+
theme: (_a = options.theme) !== null && _a !== void 0 ? _a : "light",
|
|
842
|
+
};
|
|
843
|
+
this.element = (_b = options.element) !== null && _b !== void 0 ? _b : null;
|
|
823
844
|
// 1. Resolve extensions
|
|
824
|
-
this.extensions = (0, schema_js_1.flattenExtensions)((
|
|
845
|
+
this.extensions = (0, schema_js_1.flattenExtensions)((_c = options.extensions) !== null && _c !== void 0 ? _c : []);
|
|
825
846
|
// 2. Compile schema
|
|
826
847
|
this.schema = (0, schema_js_1.compileSchema)(this.extensions);
|
|
827
848
|
// 3. Normalize initial content into JSON
|
|
@@ -830,7 +851,7 @@ class Editor {
|
|
|
830
851
|
this.commands = this.buildCommandMap();
|
|
831
852
|
// 5. Lifecycle: onCreate for each extension
|
|
832
853
|
for (const ext of this.extensions) {
|
|
833
|
-
(
|
|
854
|
+
(_d = ext.onCreate) === null || _d === void 0 ? void 0 : _d.call(ext, this);
|
|
834
855
|
}
|
|
835
856
|
// 6. Mount to DOM if an element was given
|
|
836
857
|
if (this.element) {
|
|
@@ -1117,9 +1138,10 @@ class Editor {
|
|
|
1117
1138
|
this.attachToElement();
|
|
1118
1139
|
}
|
|
1119
1140
|
attachToElement() {
|
|
1120
|
-
var _a;
|
|
1141
|
+
var _a, _b;
|
|
1121
1142
|
const el = this.element;
|
|
1122
1143
|
el.setAttribute("contenteditable", String(this.options.editable !== false));
|
|
1144
|
+
el.setAttribute("data-theme", (_a = this.options.theme) !== null && _a !== void 0 ? _a : "light");
|
|
1123
1145
|
el.classList.add("smeditor-editor");
|
|
1124
1146
|
el.addEventListener("input", this.onDomInput);
|
|
1125
1147
|
el.addEventListener("beforeinput", this.onDomBeforeInput);
|
|
@@ -1133,7 +1155,7 @@ class Editor {
|
|
|
1133
1155
|
// Keep the model selection in sync in both regular DOM and Shadow DOM.
|
|
1134
1156
|
// Chromium exposes the live shadow selection on ShadowRoot; listening to
|
|
1135
1157
|
// that root as well as Document avoids missing drag-selection events.
|
|
1136
|
-
const root = (
|
|
1158
|
+
const root = (_b = el.getRootNode) === null || _b === void 0 ? void 0 : _b.call(el);
|
|
1137
1159
|
const targets = [document];
|
|
1138
1160
|
if (root && root !== document && "addEventListener" in root)
|
|
1139
1161
|
targets.push(root);
|
|
@@ -1,15 +1,11 @@
|
|
|
1
1
|
/* SMEditor default theme tokens.
|
|
2
2
|
*
|
|
3
|
-
*
|
|
3
|
+
* Theme selection:
|
|
4
4
|
*
|
|
5
|
-
* 1. Do nothing →
|
|
6
|
-
* 2. Set `data-theme="light"` or `data-theme="dark"` on
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
* Why both: developers want auto for new users (matches the rest of
|
|
11
|
-
* their app), but a theme toggle in product UI needs explicit control
|
|
12
|
-
* that survives reloads. Supporting both lets either pattern work.
|
|
5
|
+
* 1. Do nothing → light theme.
|
|
6
|
+
* 2. Set `data-theme="light"` or `data-theme="dark"` on the editor or
|
|
7
|
+
* on an ancestor. SMEditor sets `data-theme="light"` by default when
|
|
8
|
+
* initialized without a theme option.
|
|
13
9
|
*/
|
|
14
10
|
|
|
15
11
|
/* ---------------------------------------------------------------------------
|
|
@@ -87,8 +83,7 @@
|
|
|
87
83
|
}
|
|
88
84
|
|
|
89
85
|
/* ---------------------------------------------------------------------------
|
|
90
|
-
* Dark theme — applied when explicitly tagged
|
|
91
|
-
* no explicit theme is set on the document.
|
|
86
|
+
* Dark theme — applied only when explicitly tagged.
|
|
92
87
|
* ------------------------------------------------------------------------ */
|
|
93
88
|
:host([data-theme="dark"]),
|
|
94
89
|
[data-theme="dark"] {
|
|
@@ -110,31 +105,6 @@
|
|
|
110
105
|
0 10px 20px -15px rgba(0, 0, 0, 0.45);
|
|
111
106
|
}
|
|
112
107
|
|
|
113
|
-
/* Auto-dark: only when the document root has no explicit override. The
|
|
114
|
-
* `:not([data-theme])` guard means a `data-theme="light"` setting on
|
|
115
|
-
* any ancestor wins, even at night. */
|
|
116
|
-
@media (prefers-color-scheme: dark) {
|
|
117
|
-
:root:not([data-theme="light"]),
|
|
118
|
-
:host(:not([data-theme="light"])) {
|
|
119
|
-
--sme-color-surface: #0f1115;
|
|
120
|
-
--sme-color-surface-muted: #1a1d23;
|
|
121
|
-
--sme-color-border: #2a2d35;
|
|
122
|
-
--sme-color-border-strong: #3a3f49;
|
|
123
|
-
--sme-color-text: #e7e9ee;
|
|
124
|
-
--sme-color-text-muted: #9aa0aa;
|
|
125
|
-
--sme-color-placeholder: #5c606b;
|
|
126
|
-
--sme-color-accent: #a78bfa;
|
|
127
|
-
--sme-color-accent-soft: #2a1d54;
|
|
128
|
-
--sme-color-primary: var(--sme-color-accent);
|
|
129
|
-
--sme-color-focus: #c4b5fd;
|
|
130
|
-
--sme-color-danger: #fca5a5;
|
|
131
|
-
--sme-color-danger-soft: #451a1a;
|
|
132
|
-
--sme-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.35);
|
|
133
|
-
--sme-shadow-md: 0 10px 38px -10px rgba(0, 0, 0, 0.5),
|
|
134
|
-
0 10px 20px -15px rgba(0, 0, 0, 0.45);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
108
|
/* SMEditor default theme.
|
|
139
109
|
*
|
|
140
110
|
* Imports tokens, then styles the structural classes set by core
|
|
@@ -143,6 +113,7 @@
|
|
|
143
113
|
*/
|
|
144
114
|
|
|
145
115
|
.smeditor {
|
|
116
|
+
position: relative;
|
|
146
117
|
font-family: var(--sme-font-sans);
|
|
147
118
|
color: var(--sme-color-text);
|
|
148
119
|
background: var(--sme-color-surface);
|
|
@@ -14,6 +14,9 @@ SMEditor.configure do |config|
|
|
|
14
14
|
# "starter" gives the MVP toolbar; "full" enables the larger extension set.
|
|
15
15
|
config.default_kit = "starter"
|
|
16
16
|
|
|
17
|
+
# "light" is the default. Set "dark" globally or pass theme: per editor.
|
|
18
|
+
config.default_theme = "light"
|
|
19
|
+
|
|
17
20
|
# Responsive minimum editor height. Numeric values are pixels. You can also
|
|
18
21
|
# use CSS lengths such as "24rem" or "45vh".
|
|
19
22
|
config.min_height = 320
|
|
@@ -18,6 +18,8 @@ module SMEditor
|
|
|
18
18
|
attr_accessor :allowed_tags
|
|
19
19
|
# Which bundle the form helper boots: "starter" or "full".
|
|
20
20
|
attr_accessor :default_kit
|
|
21
|
+
# Default visual theme: "light" or "dark".
|
|
22
|
+
attr_accessor :default_theme
|
|
21
23
|
# Upload endpoint used by the JavaScript adapter when uploads are enabled.
|
|
22
24
|
attr_accessor :upload_path
|
|
23
25
|
# Include the packaged JS/CSS automatically with the first editor field.
|
|
@@ -47,6 +49,7 @@ module SMEditor
|
|
|
47
49
|
table thead tbody tr td th
|
|
48
50
|
]
|
|
49
51
|
@default_kit = "starter"
|
|
52
|
+
@default_theme = "light"
|
|
50
53
|
@upload_path = "/smeditor/uploads"
|
|
51
54
|
@auto_include_assets = true
|
|
52
55
|
@min_height = "320px"
|
|
@@ -11,24 +11,28 @@ module SMEditor
|
|
|
11
11
|
# include assets in <head> can call `smeditor_assets` there and disable
|
|
12
12
|
# auto inclusion with config.auto_include_assets = false.
|
|
13
13
|
def smeditor_assets
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
14
|
+
if defined?(@_smeditor_assets_rendered) && @_smeditor_assets_rendered
|
|
15
|
+
"".html_safe
|
|
16
|
+
else
|
|
17
|
+
@_smeditor_assets_rendered = true
|
|
18
|
+
safe_join([
|
|
19
|
+
stylesheet_link_tag("smeditor", "data-turbo-track": "reload"),
|
|
20
|
+
javascript_include_tag("smeditor", defer: true, "data-turbo-track": "reload"),
|
|
21
|
+
])
|
|
22
|
+
end
|
|
21
23
|
end
|
|
22
24
|
|
|
23
25
|
# form - a Rails FormBuilder
|
|
24
26
|
# method - the model attribute (stored as an HTML string)
|
|
25
27
|
# options - :kit ("starter" | "full"), :class, :placeholder,
|
|
26
|
-
# :
|
|
28
|
+
# :theme ("light" | "dark"), :upload_url, :label,
|
|
29
|
+
# :include_assets, :min_height,
|
|
27
30
|
# :tablet_min_height, :mobile_min_height
|
|
28
31
|
def smeditor_editor(form, method, options = {})
|
|
29
32
|
object = form.object
|
|
30
33
|
current = object.respond_to?(method) ? object.public_send(method) : nil
|
|
31
34
|
kit = (options[:kit] || SMEditor.config.default_kit).to_s
|
|
35
|
+
theme = smeditor_theme(options.fetch(:theme, SMEditor.config.default_theme))
|
|
32
36
|
upload_url = options.fetch(:upload_url, smeditor_upload_url)
|
|
33
37
|
include_assets = options.fetch(:include_assets, SMEditor.config.auto_include_assets)
|
|
34
38
|
stylesheet_url = asset_path("smeditor.css")
|
|
@@ -49,6 +53,7 @@ module SMEditor
|
|
|
49
53
|
data: {
|
|
50
54
|
smeditor: true,
|
|
51
55
|
smeditor_kit: kit,
|
|
56
|
+
smeditor_theme: theme,
|
|
52
57
|
smeditor_placeholder: options[:placeholder],
|
|
53
58
|
smeditor_upload_url: upload_url,
|
|
54
59
|
smeditor_label: options[:label],
|
|
@@ -71,9 +76,20 @@ module SMEditor
|
|
|
71
76
|
|
|
72
77
|
def smeditor_css_length(value, option_name)
|
|
73
78
|
normalized = value.is_a?(Numeric) ? "#{value}px" : value.to_s.strip
|
|
74
|
-
|
|
79
|
+
if normalized.match?(CSS_LENGTH)
|
|
80
|
+
normalized
|
|
81
|
+
else
|
|
82
|
+
raise ArgumentError, "#{option_name} must be a number (pixels) or a CSS length such as 320px, 24rem, or 45vh"
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def smeditor_theme(value)
|
|
87
|
+
theme = value.to_s
|
|
88
|
+
unless %w[light dark].include?(theme)
|
|
89
|
+
raise ArgumentError, 'theme must be "light" or "dark"'
|
|
90
|
+
end
|
|
75
91
|
|
|
76
|
-
|
|
92
|
+
theme
|
|
77
93
|
end
|
|
78
94
|
|
|
79
95
|
def smeditor_upload_url
|
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: smeditor
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- SMEditor contributors
|
|
@@ -96,7 +96,7 @@ metadata:
|
|
|
96
96
|
source_code_uri: https://github.com/sCruze/smeditor/tree/main/gems/smeditor
|
|
97
97
|
changelog_uri: https://github.com/sCruze/smeditor/blob/main/gems/smeditor/CHANGELOG.md
|
|
98
98
|
bug_tracker_uri: https://github.com/sCruze/smeditor/issues
|
|
99
|
-
documentation_uri: https://rubydoc.info/gems/smeditor/0.
|
|
99
|
+
documentation_uri: https://rubydoc.info/gems/smeditor/0.3.0
|
|
100
100
|
rubygems_mfa_required: 'true'
|
|
101
101
|
rdoc_options: []
|
|
102
102
|
require_paths:
|