smeditor 0.2.5 → 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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b78483ba31701d6ff1da3c082c82396b6612812174f756d83312b13dc9f2a16d
4
- data.tar.gz: d14fac8b9b6a172bf23fddf19eb0fa6b34b3eae978884ed7cc6b70b29a8c7e2e
3
+ metadata.gz: 37d69366d661f31b412b1225d656367136e26de3ac8ad98988ceeaf251ddc942
4
+ data.tar.gz: fbc0c5332adb77f6a42b1716c403a31e3dd21d8eb79919ee214073a69caab8ed
5
5
  SHA512:
6
- metadata.gz: f05b0e4d311cc29ab173c12b84d745fb8ef50c3a956d1983bf619a901fc738157cabcfe9784a197c28ff8991f88ccedc5b74af38a161aab1b63474b19c6c0841
7
- data.tar.gz: c7ed0efe7cddecbde87182b620027ccfed65002948e14f4f68226e7ba3dc92bd1df095cd0445d3943e37193e2afd5a32159a34ff7ed19936aac5fad4104113a0
6
+ metadata.gz: 5db1e9fa62c9bf728776931b9276184d18b85c36e586efbe907fae818cd6ecf4785d8144e66b922a3e567f64db97a546d96ca3ee4ecd5b8aa3c655b7933bd496
7
+ data.tar.gz: 9426a009a1184a403397818a2f37600702dbf72c3ecaa73bd7c0ffff44ab0f638416b033cc8e3208bbce664fba1fca97182bb7410b61e5b69dcc9bbff4f4f4ec
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
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
+
3
9
  ## 0.2.5 — 2026-09-09
4
10
 
5
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).
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
@@ -595,10 +595,15 @@ function bootMount(mount) {
595
595
  const kit = mount.dataset.smeditorKit === "full" ? "full" : "starter";
596
596
  const uploadUrl = mount.dataset.smeditorUploadUrl || undefined;
597
597
  const placeholder = mount.dataset.smeditorPlaceholder || undefined;
598
- const explicitTheme = ((_b = (_a = mount.parentElement) === null || _a === void 0 ? void 0 : _a.closest("[data-theme]")) === null || _b === void 0 ? void 0 : _b.dataset.theme)
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)
599
600
  || document.documentElement.dataset.theme;
600
- if (explicitTheme === "light" || explicitTheme === "dark")
601
- mount.dataset.theme = explicitTheme;
601
+ const theme = configuredTheme === "dark" || configuredTheme === "light"
602
+ ? configuredTheme
603
+ : inheritedTheme === "dark" || inheritedTheme === "light"
604
+ ? inheritedTheme
605
+ : "light";
606
+ mount.dataset.theme = theme;
602
607
  const root = shadowRootFor(mount);
603
608
  const shell = document.createElement("div");
604
609
  shell.className = "smeditor";
@@ -621,6 +626,7 @@ function bootMount(mount) {
621
626
  extensions: kitExtensions(kit, uploadUrl),
622
627
  content: input.value || "<p></p>",
623
628
  placeholder,
629
+ theme,
624
630
  deepSelection: true,
625
631
  onUpdate: ({ editor: current }) => {
626
632
  input.value = current.getHTML();
@@ -800,7 +806,7 @@ function createEditor(options) {
800
806
  }
801
807
  class Editor {
802
808
  constructor(options) {
803
- var _a, _b, _c;
809
+ var _a, _b, _c, _d;
804
810
  this.selection = null;
805
811
  this.history = new history_js_1.History();
806
812
  this.emitter = new events_js_1.EventEmitter();
@@ -830,10 +836,13 @@ class Editor {
830
836
  // Capture the finished composed text in one shot.
831
837
  this.handleDOMInput();
832
838
  };
833
- this.options = options;
834
- this.element = (_a = options.element) !== null && _a !== void 0 ? _a : null;
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;
835
844
  // 1. Resolve extensions
836
- this.extensions = (0, schema_js_1.flattenExtensions)((_b = options.extensions) !== null && _b !== void 0 ? _b : []);
845
+ this.extensions = (0, schema_js_1.flattenExtensions)((_c = options.extensions) !== null && _c !== void 0 ? _c : []);
837
846
  // 2. Compile schema
838
847
  this.schema = (0, schema_js_1.compileSchema)(this.extensions);
839
848
  // 3. Normalize initial content into JSON
@@ -842,7 +851,7 @@ class Editor {
842
851
  this.commands = this.buildCommandMap();
843
852
  // 5. Lifecycle: onCreate for each extension
844
853
  for (const ext of this.extensions) {
845
- (_c = ext.onCreate) === null || _c === void 0 ? void 0 : _c.call(ext, this);
854
+ (_d = ext.onCreate) === null || _d === void 0 ? void 0 : _d.call(ext, this);
846
855
  }
847
856
  // 6. Mount to DOM if an element was given
848
857
  if (this.element) {
@@ -1129,9 +1138,10 @@ class Editor {
1129
1138
  this.attachToElement();
1130
1139
  }
1131
1140
  attachToElement() {
1132
- var _a;
1141
+ var _a, _b;
1133
1142
  const el = this.element;
1134
1143
  el.setAttribute("contenteditable", String(this.options.editable !== false));
1144
+ el.setAttribute("data-theme", (_a = this.options.theme) !== null && _a !== void 0 ? _a : "light");
1135
1145
  el.classList.add("smeditor-editor");
1136
1146
  el.addEventListener("input", this.onDomInput);
1137
1147
  el.addEventListener("beforeinput", this.onDomBeforeInput);
@@ -1145,7 +1155,7 @@ class Editor {
1145
1155
  // Keep the model selection in sync in both regular DOM and Shadow DOM.
1146
1156
  // Chromium exposes the live shadow selection on ShadowRoot; listening to
1147
1157
  // that root as well as Document avoids missing drag-selection events.
1148
- const root = (_a = el.getRootNode) === null || _a === void 0 ? void 0 : _a.call(el);
1158
+ const root = (_b = el.getRootNode) === null || _b === void 0 ? void 0 : _b.call(el);
1149
1159
  const targets = [document];
1150
1160
  if (root && root !== document && "addEventListener" in root)
1151
1161
  targets.push(root);
@@ -1,15 +1,11 @@
1
1
  /* SMEditor default theme tokens.
2
2
  *
3
- * Two ways to pick a theme:
3
+ * Theme selection:
4
4
  *
5
- * 1. Do nothing → the editor follows `prefers-color-scheme`.
6
- * 2. Set `data-theme="light"` or `data-theme="dark"` on any element
7
- * that wraps the editor (or on <html>). The explicit attribute
8
- * wins over the media query.
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, OR via media query when
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
@@ -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
- return "".html_safe if defined?(@_smeditor_assets_rendered) && @_smeditor_assets_rendered
15
-
16
- @_smeditor_assets_rendered = true
17
- safe_join([
18
- stylesheet_link_tag("smeditor", "data-turbo-track": "reload"),
19
- javascript_include_tag("smeditor", defer: true, "data-turbo-track": "reload"),
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
- # :upload_url, :label, :include_assets, :min_height,
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
- return normalized if normalized.match?(CSS_LENGTH)
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
- raise ArgumentError, "#{option_name} must be a number (pixels) or a CSS length such as 320px, 24rem, or 45vh"
92
+ theme
77
93
  end
78
94
 
79
95
  def smeditor_upload_url
@@ -2,6 +2,6 @@
2
2
 
3
3
  module SMEditor
4
4
  module Rails
5
- VERSION = "0.2.5"
5
+ VERSION = "0.3.0"
6
6
  end
7
7
  end
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.2.5
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.2.5
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: