@webround/editor 0.0.0-stage → 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.
Files changed (227) hide show
  1. package/dist/blocks.d.ts +90 -0
  2. package/dist/blocks.js +119 -0
  3. package/dist/bridge.d.ts +96 -0
  4. package/dist/bridge.js +970 -0
  5. package/dist/check/aria.d.ts +19 -0
  6. package/dist/check/aria.js +106 -0
  7. package/dist/check/cli.d.ts +3 -0
  8. package/dist/check/cli.js +30 -0
  9. package/dist/check/css.d.ts +13 -0
  10. package/dist/check/css.js +63 -0
  11. package/dist/check/html.d.ts +8 -0
  12. package/dist/check/html.js +29 -0
  13. package/dist/check/index.d.ts +4 -0
  14. package/dist/check/index.js +5 -0
  15. package/dist/check/links.d.ts +7 -0
  16. package/dist/check/links.js +23 -0
  17. package/dist/check/orphans.d.ts +20 -0
  18. package/dist/check/orphans.js +57 -0
  19. package/dist/check/producer-code.d.ts +53 -0
  20. package/dist/check/producer-code.js +379 -0
  21. package/dist/check/producer-dom.d.ts +21 -0
  22. package/dist/check/producer-dom.js +52 -0
  23. package/dist/check/project.d.ts +20 -0
  24. package/dist/check/project.js +51 -0
  25. package/dist/check/rules.d.ts +7 -0
  26. package/dist/check/rules.js +517 -0
  27. package/dist/check/types.d.ts +160 -0
  28. package/dist/check/types.js +22 -0
  29. package/dist/components/WrAltcha.d.ts +5 -0
  30. package/dist/components/WrAltcha.js +8 -0
  31. package/dist/components/WrButton.d.ts +8 -0
  32. package/dist/components/WrButton.js +34 -0
  33. package/dist/components/WrForm.d.ts +6 -0
  34. package/dist/components/WrForm.js +29 -0
  35. package/dist/components/WrFormPrivacy.d.ts +4 -0
  36. package/dist/components/WrFormPrivacy.js +7 -0
  37. package/dist/components/WrFormSuccess.d.ts +4 -0
  38. package/dist/components/WrFormSuccess.js +12 -0
  39. package/dist/components/WrIframe.d.ts +12 -0
  40. package/dist/components/WrIframe.js +30 -0
  41. package/dist/components/WrImage.d.ts +9 -0
  42. package/dist/components/WrImage.js +28 -0
  43. package/dist/components/WrInput.d.ts +8 -0
  44. package/dist/components/WrInput.js +29 -0
  45. package/dist/components/WrLink.d.ts +10 -0
  46. package/dist/components/WrLink.js +38 -0
  47. package/dist/components/WrNav.d.ts +26 -0
  48. package/dist/components/WrNav.js +30 -0
  49. package/dist/components/WrSection.d.ts +11 -0
  50. package/dist/components/WrSection.js +5 -0
  51. package/dist/components/WrShared.d.ts +12 -0
  52. package/dist/components/WrShared.js +20 -0
  53. package/dist/components/WrText.d.ts +11 -0
  54. package/dist/components/WrText.js +41 -0
  55. package/dist/components/WrVideo.d.ts +9 -0
  56. package/dist/components/WrVideo.js +73 -0
  57. package/dist/components/text-editor/CodeThemeContext.d.ts +8 -0
  58. package/dist/components/text-editor/CodeThemeContext.js +10 -0
  59. package/dist/components/text-editor/ExampleTheme.d.ts +77 -0
  60. package/dist/components/text-editor/ExampleTheme.js +76 -0
  61. package/dist/components/text-editor/ToolbarContext.d.ts +17 -0
  62. package/dist/components/text-editor/ToolbarContext.js +32 -0
  63. package/dist/components/text-editor/WrTextEditor.d.ts +13 -0
  64. package/dist/components/text-editor/WrTextEditor.js +48 -0
  65. package/dist/components/text-editor/common/constants.d.ts +109 -0
  66. package/dist/components/text-editor/common/constants.js +58 -0
  67. package/dist/components/text-editor/common/surface.d.ts +3 -0
  68. package/dist/components/text-editor/common/surface.js +8 -0
  69. package/dist/components/text-editor/common/utils.d.ts +7 -0
  70. package/dist/components/text-editor/common/utils.js +78 -0
  71. package/dist/components/text-editor/hooks/useLexicalCommitOnBlur.d.ts +5 -0
  72. package/dist/components/text-editor/hooks/useLexicalCommitOnBlur.js +41 -0
  73. package/dist/components/text-editor/hooks/useLexicalMarkdown.d.ts +33 -0
  74. package/dist/components/text-editor/hooks/useLexicalMarkdown.js +284 -0
  75. package/dist/components/text-editor/nodes/CodeBlockNode.d.ts +23 -0
  76. package/dist/components/text-editor/nodes/CodeBlockNode.js +138 -0
  77. package/dist/components/text-editor/nodes/HorizontalRuleNode.d.ts +13 -0
  78. package/dist/components/text-editor/nodes/HorizontalRuleNode.js +22 -0
  79. package/dist/components/text-editor/plugins/CodeBlockPlugin.d.ts +2 -0
  80. package/dist/components/text-editor/plugins/CodeBlockPlugin.js +38 -0
  81. package/dist/components/text-editor/plugins/FontPlugin.d.ts +12 -0
  82. package/dist/components/text-editor/plugins/FontPlugin.js +114 -0
  83. package/dist/components/text-editor/plugins/FontSizePlugin.d.ts +7 -0
  84. package/dist/components/text-editor/plugins/FontSizePlugin.js +105 -0
  85. package/dist/components/text-editor/plugins/FontWeightPlugin.d.ts +6 -0
  86. package/dist/components/text-editor/plugins/FontWeightPlugin.js +103 -0
  87. package/dist/components/text-editor/plugins/HTMLTagPlugin.d.ts +7 -0
  88. package/dist/components/text-editor/plugins/HTMLTagPlugin.js +82 -0
  89. package/dist/components/text-editor/plugins/HorizontalRulePlugin.d.ts +2 -0
  90. package/dist/components/text-editor/plugins/HorizontalRulePlugin.js +35 -0
  91. package/dist/components/text-editor/plugins/LineSpacingPlugin.d.ts +7 -0
  92. package/dist/components/text-editor/plugins/LineSpacingPlugin.js +83 -0
  93. package/dist/components/text-editor/plugins/LinkPlugin.d.ts +8 -0
  94. package/dist/components/text-editor/plugins/LinkPlugin.js +183 -0
  95. package/dist/components/text-editor/plugins/ListToolbarPlugin.d.ts +7 -0
  96. package/dist/components/text-editor/plugins/ListToolbarPlugin.js +40 -0
  97. package/dist/components/text-editor/plugins/LoadContentPlugin.d.ts +4 -0
  98. package/dist/components/text-editor/plugins/LoadContentPlugin.js +63 -0
  99. package/dist/components/text-editor/plugins/MarkdownExportPlugin.d.ts +4 -0
  100. package/dist/components/text-editor/plugins/MarkdownExportPlugin.js +17 -0
  101. package/dist/components/text-editor/plugins/MarkdownPastePlugin.d.ts +2 -0
  102. package/dist/components/text-editor/plugins/MarkdownPastePlugin.js +45 -0
  103. package/dist/components/text-editor/plugins/SanitizePastePlugin.d.ts +3 -0
  104. package/dist/components/text-editor/plugins/SanitizePastePlugin.js +40 -0
  105. package/dist/components/text-editor/plugins/TextAlignPlugin.d.ts +7 -0
  106. package/dist/components/text-editor/plugins/TextAlignPlugin.js +65 -0
  107. package/dist/components/text-editor/plugins/TextColorPlugin.d.ts +7 -0
  108. package/dist/components/text-editor/plugins/TextColorPlugin.js +117 -0
  109. package/dist/components/text-editor/plugins/ToolbarPlugin.d.ts +10 -0
  110. package/dist/components/text-editor/plugins/ToolbarPlugin.js +73 -0
  111. package/dist/components/text-editor/theme-colors.d.ts +11 -0
  112. package/dist/components/text-editor/theme-colors.js +28 -0
  113. package/dist/context/WrAppContext.d.ts +35 -0
  114. package/dist/context/WrAppContext.js +113 -0
  115. package/dist/context/WrLayout.d.ts +15 -0
  116. package/dist/context/WrLayout.js +22 -0
  117. package/dist/context/WrPageContext.d.ts +28 -0
  118. package/dist/context/WrPageContext.js +282 -0
  119. package/dist/context/restoreValues.d.ts +2 -0
  120. package/dist/context/restoreValues.js +6 -0
  121. package/dist/contrast.d.ts +45 -0
  122. package/dist/contrast.js +122 -0
  123. package/dist/drop.d.ts +7 -0
  124. package/dist/drop.js +51 -0
  125. package/dist/gaps.d.ts +14 -0
  126. package/dist/gaps.js +50 -0
  127. package/dist/hooks/useWrMessage.d.ts +5 -0
  128. package/dist/hooks/useWrMessage.js +24 -0
  129. package/dist/hooks/useWrSave.d.ts +2 -0
  130. package/dist/hooks/useWrSave.js +11 -0
  131. package/dist/index.css +467 -0
  132. package/dist/index.d.ts +20 -0
  133. package/dist/index.js +20 -0
  134. package/dist/interactive.d.ts +8 -0
  135. package/dist/interactive.js +13 -0
  136. package/dist/measure-rules.d.ts +34 -0
  137. package/dist/measure-rules.js +54 -0
  138. package/dist/measure.d.ts +11 -0
  139. package/dist/measure.js +139 -0
  140. package/dist/messages.d.ts +860 -0
  141. package/dist/messages.js +2 -0
  142. package/dist/nav.d.ts +5 -0
  143. package/dist/nav.js +10 -0
  144. package/dist/operations/animations-store.d.ts +6 -0
  145. package/dist/operations/animations-store.js +63 -0
  146. package/dist/operations/cli.d.ts +3 -0
  147. package/dist/operations/cli.js +52 -0
  148. package/dist/operations/comments-store.d.ts +17 -0
  149. package/dist/operations/comments-store.js +62 -0
  150. package/dist/operations/context.d.ts +12 -0
  151. package/dist/operations/context.js +35 -0
  152. package/dist/operations/define.d.ts +81 -0
  153. package/dist/operations/define.js +86 -0
  154. package/dist/operations/index.d.ts +6 -0
  155. package/dist/operations/index.js +6 -0
  156. package/dist/operations/ops/check.d.ts +5 -0
  157. package/dist/operations/ops/check.js +53 -0
  158. package/dist/operations/ops/comments.d.ts +6 -0
  159. package/dist/operations/ops/comments.js +133 -0
  160. package/dist/operations/ops/content.d.ts +16 -0
  161. package/dist/operations/ops/content.js +73 -0
  162. package/dist/operations/ops/json.d.ts +4 -0
  163. package/dist/operations/ops/json.js +27 -0
  164. package/dist/operations/ops/langs.d.ts +4 -0
  165. package/dist/operations/ops/langs.js +32 -0
  166. package/dist/operations/ops/nodes.d.ts +8 -0
  167. package/dist/operations/ops/nodes.js +270 -0
  168. package/dist/operations/ops/pages.d.ts +8 -0
  169. package/dist/operations/ops/pages.js +154 -0
  170. package/dist/operations/ops/project.d.ts +36 -0
  171. package/dist/operations/ops/project.js +97 -0
  172. package/dist/operations/ops/seo.d.ts +17 -0
  173. package/dist/operations/ops/seo.js +358 -0
  174. package/dist/operations/ops/style.d.ts +76 -0
  175. package/dist/operations/ops/style.js +268 -0
  176. package/dist/operations/ops/stylesets.d.ts +9 -0
  177. package/dist/operations/ops/stylesets.js +145 -0
  178. package/dist/operations/ops/theme.d.ts +6 -0
  179. package/dist/operations/ops/theme.js +304 -0
  180. package/dist/operations/registry.d.ts +8 -0
  181. package/dist/operations/registry.js +19 -0
  182. package/dist/operations/stylesets-store.d.ts +50 -0
  183. package/dist/operations/stylesets-store.js +216 -0
  184. package/dist/registry.d.ts +5 -0
  185. package/dist/registry.js +24 -0
  186. package/dist/save.d.ts +65 -0
  187. package/dist/save.js +86 -0
  188. package/dist/schema.d.ts +69 -0
  189. package/dist/schema.js +110 -0
  190. package/dist/scope.d.ts +41 -0
  191. package/dist/scope.js +44 -0
  192. package/dist/vite-plugins/ast.d.ts +3 -0
  193. package/dist/vite-plugins/ast.js +238 -0
  194. package/dist/vite-plugins/core-swap.d.ts +3 -0
  195. package/dist/vite-plugins/core-swap.js +57 -0
  196. package/dist/vite-plugins/create-page.d.ts +4 -0
  197. package/dist/vite-plugins/create-page.js +59 -0
  198. package/dist/vite-plugins/guard.d.ts +15 -0
  199. package/dist/vite-plugins/guard.js +35 -0
  200. package/dist/vite-plugins/head.d.ts +13 -0
  201. package/dist/vite-plugins/head.js +53 -0
  202. package/dist/vite-plugins/index.d.ts +7 -0
  203. package/dist/vite-plugins/index.js +7 -0
  204. package/dist/vite-plugins/jsx-tree.d.ts +118 -0
  205. package/dist/vite-plugins/jsx-tree.js +686 -0
  206. package/dist/vite-plugins/langs.d.ts +3 -0
  207. package/dist/vite-plugins/langs.js +44 -0
  208. package/dist/vite-plugins/operations.d.ts +10 -0
  209. package/dist/vite-plugins/operations.js +66 -0
  210. package/dist/vite-plugins/pages-api.d.ts +3 -0
  211. package/dist/vite-plugins/pages-api.js +112 -0
  212. package/dist/vite-plugins/pages.d.ts +3 -0
  213. package/dist/vite-plugins/pages.js +64 -0
  214. package/dist/vite-plugins/read.d.ts +3 -0
  215. package/dist/vite-plugins/read.js +45 -0
  216. package/dist/vite-plugins/seo-files.d.ts +8 -0
  217. package/dist/vite-plugins/seo-files.js +42 -0
  218. package/dist/vite-plugins/theme.d.ts +3 -0
  219. package/dist/vite-plugins/theme.js +207 -0
  220. package/dist/vite-plugins/update.d.ts +3 -0
  221. package/dist/vite-plugins/update.js +46 -0
  222. package/dist/vite-plugins/wr-plugins.d.ts +15 -0
  223. package/dist/vite-plugins/wr-plugins.js +34 -0
  224. package/dist/vite-plugins.d.ts +11 -0
  225. package/dist/vite-plugins.js +518 -0
  226. package/package.json +96 -5
  227. package/README.md +0 -3
@@ -0,0 +1,268 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import { WR_BREAKPOINT_IDS, WR_DEFAULT_COLOR_TOKENS, WR_STATE_IDS, WrStyleError, wrParseClassName, wrStyleApply, wrStyleApplyEverywhere, wrStyleCatalog, wrStyleCopyTo, wrStyleDescribe, wrStyleProperties, wrStyleRead, wrStyleUnmanaged, } from '@webround/core/style';
4
+ import { findNode, readJsxLiteral, writeJsxLiteral } from '../../vite-plugins/jsx-tree.js';
5
+ import { findStyleSetUsages, readStyleSet, readStyleSets, writeStyleSet } from '../stylesets-store.js';
6
+ import { ensureEntryAnimations } from '../animations-store.js';
7
+ import { defineOperation, flag, invalid, OperationError, requiredText, text } from '../define.js';
8
+ import { scopeOfInput } from './project.js';
9
+ // How an element looks: its classes (Tailwind), said as a property (padding, text color, width…) at a breakpoint (base, md…) with a value that has a
10
+ // name (4, accent, center). ONE table says all of it (@webround/core/style): this file only reads the element's `className`, asks the table, and writes the
11
+ // className back, so the editor's panel, `wr`, an MCP client and the API all say the same and change the same text.
12
+ const SCOPE_PARAMS = [
13
+ { name: 'page', type: 'string', help: 'the page (its name, e.g. home) or a shared component (_shared/header, _shared/footer, _shared/<name> as declared in config/shared.json)' },
14
+ { name: 'file', type: 'string', help: 'instead of page: the code file, relative to src/ (what the editor\'s own page sends)' },
15
+ ];
16
+ /** Who is styled: an element (its id, in a page or a shared component) or, instead, a shared style by its name (`set`): the same ops, the same rules. */
17
+ const TARGET_PARAMS = [
18
+ ...SCOPE_PARAMS,
19
+ { name: 'id', type: 'string', positional: true, help: 'the element id (its data-wr-id)' },
20
+ { name: 'set', type: 'string', help: 'instead of id: the name of a shared style (see styleset-list), to change what every element that uses it looks like' },
21
+ ];
22
+ const PROPERTY_IDS = wrStyleProperties().map(p => p.id);
23
+ const readSrcFile = (ctx, name) => {
24
+ try {
25
+ return fs.readFileSync(path.join(ctx.srcRoot, name), 'utf-8');
26
+ }
27
+ catch {
28
+ return '';
29
+ }
30
+ };
31
+ /**
32
+ * The colors the project's theme names, and how they look: the `--color-*` of its `@theme` (in theme.css, or in index.css for an older site: what makes `text-accent` a class), and
33
+ * the color each one comes to (`--color-accent: var(--wr-color-accent)` → the value of `--wr-color-accent` in theme.css, the light theme's).
34
+ * The starter site's names when the stylesheet says none.
35
+ */
36
+ export function themeColorsOf(ctx) {
37
+ const css = readSrcFile(ctx, 'index.css');
38
+ const theme = readSrcFile(ctx, 'theme.css');
39
+ const tokens = [];
40
+ const swatches = {};
41
+ const wrValue = (name) => new RegExp(`${name.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&')}\\s*:\\s*([^;}]+)`).exec(`${theme}\n${css}`)?.[1]?.trim();
42
+ for (const block of `${theme}\n${css}`.matchAll(/@theme[^{]*\{([^}]*)\}/g)) {
43
+ for (const match of block[1].matchAll(/--color-([a-z0-9-]+)\s*:\s*([^;]+);/gi)) {
44
+ if (!tokens.includes(match[1]))
45
+ tokens.push(match[1]);
46
+ const given = match[2].trim();
47
+ const viaVariable = /^var\((--[a-z0-9-]+)\)$/i.exec(given);
48
+ const value = viaVariable ? wrValue(viaVariable[1]) : given;
49
+ if (value && /^(#[0-9a-f]{3,8}|(rgb|hsl|oklch|oklab)a?\([^)]*\))$/i.test(value))
50
+ swatches[match[1]] = value;
51
+ }
52
+ }
53
+ return tokens.length > 0 ? { tokens, swatches } : { tokens: [...WR_DEFAULT_COLOR_TOKENS], swatches: {} };
54
+ }
55
+ export function element(ctx, input) {
56
+ const scope = scopeOfInput(ctx, input);
57
+ const id = requiredText(input, 'id');
58
+ const file = ctx.resolveInSrc(scope.file);
59
+ if (!fs.existsSync(file))
60
+ throw new OperationError(404, 'file-not-found', `${scope.file} does not exist`);
61
+ const found = findNode(ctx.project(), file, id);
62
+ if (!found)
63
+ throw new OperationError(404, 'node-not-found', `no element with id "${id}" in ${scope.file}`);
64
+ return { found, id, scope };
65
+ }
66
+ /** The element's classes when they are a plain text (or there are none); an expression (`cn(…)`, a template) is the code's, and is not rewritten. */
67
+ export function classesOf(found) {
68
+ if (!found.node.getAttribute('className'))
69
+ return { className: '' };
70
+ const literal = readJsxLiteral(found.node, 'className');
71
+ if (literal.present && typeof literal.value === 'string')
72
+ return { className: literal.value };
73
+ return { className: null, reason: 'the classes of this element are decided by code (an expression, not a plain text), so they are not changed from here' };
74
+ }
75
+ export const optionsOf = (ctx) => {
76
+ const { tokens, swatches } = themeColorsOf(ctx);
77
+ return { colorTokens: tokens, colorSwatches: swatches };
78
+ };
79
+ /** What a className says for each state (hover, focus, pressed) that it says something for: the states of the properties that can have one. */
80
+ export function statesOf(className, options) {
81
+ const found = {};
82
+ for (const id of WR_STATE_IDS) {
83
+ const entries = wrStyleRead(className, options, id);
84
+ if (entries.length > 0)
85
+ found[id] = wrStyleDescribe(entries, options);
86
+ }
87
+ return found;
88
+ }
89
+ function applyErrors(run) {
90
+ try {
91
+ return run();
92
+ }
93
+ catch (err) {
94
+ if (err instanceof WrStyleError)
95
+ throw invalid(err.message, err.code);
96
+ throw err;
97
+ }
98
+ }
99
+ export const styleCatalog = defineOperation({
100
+ name: 'style-catalog',
101
+ readOnly: true,
102
+ summary: 'Every style property an element can have (padding, text color, width…), with what each one means, its values (with their CSS meaning) and the breakpoints they can be set at. Read this to know what style-set accepts.',
103
+ run: (_input, ctx) => wrStyleCatalog(optionsOf(ctx)),
104
+ });
105
+ export const styleGet = defineOperation({
106
+ name: 'style-get',
107
+ readOnly: true,
108
+ summary: 'How an element is styled: for each property set on it, at which breakpoint and to what (in words, with its CSS meaning and the Tailwind class), plus the classes this does not manage (hover:, dark:, your own) and the shared styles it uses (`sets`). `editable: false` when the element\'s classes are written by code. With `set` instead of id: the same for a shared style.',
109
+ params: TARGET_PARAMS,
110
+ run: (input, ctx) => {
111
+ const options = optionsOf(ctx);
112
+ const setName = text(input, 'set');
113
+ if (setName !== undefined) {
114
+ const set = readStyleSet(ctx, setName);
115
+ return { set: set.name, class: set.class, editable: true, className: set.classes, styles: wrStyleDescribe(wrStyleRead(set.classes, options), options), unmanaged: wrStyleUnmanaged(set.classes, options), states: statesOf(set.classes, options), usedBy: findStyleSetUsages(ctx, set.class).length };
116
+ }
117
+ const { found, id } = element(ctx, input);
118
+ const classes = classesOf(found);
119
+ if (classes.className === null)
120
+ return { id, editable: false, reason: classes.reason, className: null, styles: [], unmanaged: [], states: {}, sets: [] };
121
+ return {
122
+ id, editable: true, className: classes.className,
123
+ styles: wrStyleDescribe(wrStyleRead(classes.className, options), options),
124
+ unmanaged: wrStyleUnmanaged(classes.className, options),
125
+ states: statesOf(classes.className, options),
126
+ sets: setsOf(ctx, classes.className),
127
+ };
128
+ },
129
+ });
130
+ /** The shared styles an element's classes use. */
131
+ export function setsOf(ctx, className) {
132
+ const tokens = new Set(wrParseClassName(className).map(t => t.raw));
133
+ return readStyleSets(ctx).filter(set => tokens.has(set.class)).map(set => ({ name: set.name, class: set.class }));
134
+ }
135
+ function changesOf(input) {
136
+ const given = input.changes;
137
+ if (given !== undefined && given !== null) {
138
+ if (!Array.isArray(given) || given.length === 0)
139
+ throw invalid('changes must be a list of { property, breakpoint, value }');
140
+ return given.map((entry, index) => {
141
+ if (typeof entry !== 'object' || entry === null || Array.isArray(entry))
142
+ throw invalid(`changes[${index}] must be { property, breakpoint, value }`);
143
+ const { property, breakpoint, value, state } = entry;
144
+ if (typeof property !== 'string')
145
+ throw invalid(`changes[${index}].property must be text`);
146
+ if (typeof breakpoint !== 'string')
147
+ throw invalid(`changes[${index}].breakpoint must be text (${WR_BREAKPOINT_IDS.join(', ')})`);
148
+ if (value !== null && typeof value !== 'string')
149
+ throw invalid(`changes[${index}].value must be text, or null to take it away`);
150
+ if (state !== undefined && state !== null && typeof state !== 'string')
151
+ throw invalid(`changes[${index}].state must be text (${WR_STATE_IDS.join(', ')})`);
152
+ return { property, breakpoint: breakpoint, value, ...(state ? { state: state } : {}) };
153
+ });
154
+ }
155
+ const property = requiredText(input, 'property');
156
+ const value = input.value;
157
+ if (value === undefined)
158
+ throw invalid('value required (a value of the property, or null to take it away)');
159
+ if (value !== null && typeof value !== 'string' && typeof value !== 'number')
160
+ throw invalid('value must be text, or null to take it away');
161
+ const state = text(input, 'state');
162
+ return [{ property, breakpoint: (text(input, 'breakpoint') ?? 'base'), value: value === null ? null : String(value), ...(state ? { state: state } : {}) }];
163
+ }
164
+ export const styleSet = defineOperation({
165
+ name: 'style-set',
166
+ summary: 'Changes how an element looks: one property at one breakpoint ({ property: "padding-x", breakpoint: "md", value: "6" }), or several at once (changes). A value of null takes it away at that breakpoint. Only the classes of that property and breakpoint change; everything else in the element\'s classes stays. Read style-catalog for the properties and values.',
167
+ params: [
168
+ ...TARGET_PARAMS,
169
+ { name: 'property', type: 'string', choices: PROPERTY_IDS, help: 'what to change (see style-catalog)' },
170
+ { name: 'breakpoint', type: 'string', choices: [...WR_BREAKPOINT_IDS], default: 'base', help: 'from which screen width up it holds: base (every screen), sm, md (tablet), lg (desktop), xl' },
171
+ { name: 'value', type: 'any', help: 'the value (4, accent, center… see style-catalog), or null to take the property away at that breakpoint. Spacing and sizes take any number, not only the listed ones (27 is px-27), and a width or height a fraction (3/7)' },
172
+ { name: 'state', type: 'string', choices: [...WR_STATE_IDS], help: 'for a property that can change with one (see style-catalog, `states`): the state it is for: hover, focus or active (pressed). Without it, always' },
173
+ { name: 'changes', type: 'array', help: 'instead of property, breakpoint and value: a list of { property, breakpoint, value, state? }, all made as one change' },
174
+ { name: 'everywhere', type: 'boolean', default: false, help: 'make the change hold on every screen: it is made at the breakpoint named, and then what the element looks like there (for those properties only) is what all screens have, the overrides of the other screens taken away; a null value takes it away from every screen' },
175
+ ],
176
+ run: (input, ctx) => changeClasses(ctx, input, (className, options) => {
177
+ const next = (flag(input, 'everywhere') ? wrStyleApplyEverywhere : wrStyleApply)(className, changesOf(input), options);
178
+ ensureEntryAnimations(ctx, next);
179
+ return next;
180
+ }),
181
+ });
182
+ /**
183
+ * The step every change of classes takes: find who is styled (an element, or a shared style by `set`), refuse classes that are code, ask `next` for the text, write it if it changed, say what it is now.
184
+ * What a change IS (a value, a copy between screens) is the table's; this is only reading and writing the file.
185
+ */
186
+ export function changeClasses(ctx, input, next) {
187
+ const options = optionsOf(ctx);
188
+ const setName = text(input, 'set');
189
+ if (setName !== undefined) {
190
+ const set = readStyleSet(ctx, setName);
191
+ const written = applyErrors(() => next(set.classes, options));
192
+ const changed = written !== set.classes;
193
+ if (changed)
194
+ writeStyleSet(ctx, set.name, wrParseClassName(written).map(t => t.raw));
195
+ return {
196
+ set: set.name, changed, previous: set.classes, className: written,
197
+ styles: wrStyleDescribe(wrStyleRead(written, options), options),
198
+ unmanaged: wrStyleUnmanaged(written, options),
199
+ states: statesOf(written, options),
200
+ usedBy: findStyleSetUsages(ctx, set.class).length,
201
+ };
202
+ }
203
+ const { found, id } = element(ctx, input);
204
+ const classes = classesOf(found);
205
+ if (classes.className === null)
206
+ throw new OperationError(409, 'class-not-editable', classes.reason);
207
+ const written = applyErrors(() => next(classes.className, options));
208
+ const changed = written !== classes.className;
209
+ if (changed) {
210
+ writeJsxLiteral(found.node, 'className', written === '' ? null : written);
211
+ found.node.getSourceFile().saveSync();
212
+ }
213
+ return {
214
+ id, changed, previous: classes.className, className: written,
215
+ styles: wrStyleDescribe(wrStyleRead(written, options), options),
216
+ unmanaged: wrStyleUnmanaged(written, options),
217
+ states: statesOf(written, options),
218
+ sets: setsOf(ctx, written),
219
+ };
220
+ }
221
+ export const styleCopy = defineOperation({
222
+ name: 'style-copy',
223
+ summary: 'Copies the style classes of one screen (and one state) to another, or to all of them. to: a breakpoint (the classes of `from` are written there: the same properties are replaced, nothing else changes) or "all" (one clean list: the look the element has at `from` becomes what every screen has, and every breakpoint class is taken away; from "base" that only removes the overrides). hover:, dark: and the project\'s own classes are never touched.',
224
+ params: [
225
+ ...TARGET_PARAMS,
226
+ { name: 'from', type: 'string', choices: [...WR_BREAKPOINT_IDS], required: true, help: 'the screen to copy from: base (every screen), mobile, sm, md (tablet), lg (desktop), xl' },
227
+ { name: 'to', type: 'string', choices: [...WR_BREAKPOINT_IDS, 'all'], required: true, help: 'the screen to copy to, or "all" for one clean list that every screen shares' },
228
+ { name: 'state', type: 'string', choices: [...WR_STATE_IDS], help: 'copy the classes of that state (hover, focus, active) instead of the ones that hold always' },
229
+ ],
230
+ run: (input, ctx) => {
231
+ const from = requiredText(input, 'from');
232
+ const to = requiredText(input, 'to');
233
+ const state = text(input, 'state');
234
+ return changeClasses(ctx, input, (className, options) => wrStyleCopyTo(className, from, to, options, undefined, state));
235
+ },
236
+ });
237
+ /**
238
+ * Puts an element's classes back exactly as they were: what the editor's undo and redo need, because what a change replaced (padding replaces padding-x,
239
+ * a custom width…) cannot be said as another change. Only for the editor's page: an agent has style-set, which says what it means.
240
+ */
241
+ export const styleRestore = defineOperation({
242
+ name: 'style-restore',
243
+ expose: 'editor',
244
+ summary: 'Sets an element\'s classes to exactly this text (the editor\'s undo and redo).',
245
+ params: [
246
+ ...SCOPE_PARAMS,
247
+ { name: 'id', type: 'string', positional: true, required: true, help: 'the element id' },
248
+ { name: 'className', type: 'string', required: true, help: 'the classes, as they are to be; an empty text takes the attribute away' },
249
+ ],
250
+ run: (input, ctx) => {
251
+ const { found, id } = element(ctx, input);
252
+ const classes = classesOf(found);
253
+ if (classes.className === null)
254
+ throw new OperationError(409, 'class-not-editable', classes.reason);
255
+ const className = text(input, 'className', { required: false });
256
+ if (className === undefined)
257
+ throw invalid('className required');
258
+ const next = className.trim().split(/\s+/).filter(Boolean).join(' ');
259
+ const changed = next !== classes.className;
260
+ if (changed) {
261
+ writeJsxLiteral(found.node, 'className', next === '' ? null : next);
262
+ found.node.getSourceFile().saveSync();
263
+ }
264
+ return { id, changed, previous: classes.className, className: next };
265
+ },
266
+ });
267
+ export const STYLE_OPERATIONS = [styleCatalog, styleGet, styleSet, styleCopy, styleRestore];
268
+ //# sourceMappingURL=style.js.map
@@ -0,0 +1,9 @@
1
+ export declare const stylesetList: import("../define.js").Operation;
2
+ export declare const stylesetCreate: import("../define.js").Operation;
3
+ export declare const stylesetApply: import("../define.js").Operation;
4
+ export declare const stylesetDetach: import("../define.js").Operation;
5
+ export declare const stylesetDetachAll: import("../define.js").Operation;
6
+ export declare const stylesetRename: import("../define.js").Operation;
7
+ export declare const stylesetDelete: import("../define.js").Operation;
8
+ export declare const STYLESET_OPERATIONS: import("../define.js").Operation[];
9
+ //# sourceMappingURL=stylesets.d.ts.map
@@ -0,0 +1,145 @@
1
+ import { wrParseClassName, wrStyleDescribe, wrStyleLayer, wrStyleRead, wrStyleUnmanaged } from '@webround/core/style';
2
+ import { defineOperation, flag, invalid, OperationError, requiredText, text } from '../define.js';
3
+ import { checkStyleSetName, findStyleSetUsages, readStyleSet, readStyleSets, removeStyleSet, renameStyleSet, replaceStyleSetUsages, STYLESET_PREFIX, styleSetClass, unknownUtilities, writeStyleSet, } from '../stylesets-store.js';
4
+ import { changeClasses, classesOf, element, optionsOf, setsOf, themeColorsOf } from './style.js';
5
+ // Shared styles: a name for a group of Tailwind classes, kept in `src/theme.css` (see stylesets-store.ts). Here is what a person, an agent or the editor can do with them: list them, make one
6
+ // from an element, put one on an element (it replaces what the element had), write one out again on an element or on every element that uses it, take one away once nothing uses it. What a set
7
+ // LOOKS like is edited with style-get / style-set / style-copy and their `set` parameter: the same table, the same words.
8
+ const ELEMENT_PARAMS = [
9
+ { name: 'page', type: 'string', help: 'the page (its name, e.g. home) or a shared component (_shared/header, _shared/footer, _shared/<name> as declared in config/shared.json)' },
10
+ { name: 'file', type: 'string', help: 'instead of page: the code file, relative to src/ (what the editor\'s own page sends)' },
11
+ { name: 'id', type: 'string', positional: true, help: 'the element id (its data-wr-id)' },
12
+ ];
13
+ const describe = (ctx, set) => {
14
+ const options = optionsOf(ctx);
15
+ const usages = findStyleSetUsages(ctx, set.class).map(u => u.usage);
16
+ return {
17
+ name: set.name, class: set.class, classes: set.classes,
18
+ styles: wrStyleDescribe(wrStyleRead(set.classes, options), options).map(d => d.sentence),
19
+ usedBy: usages.length, places: usages,
20
+ };
21
+ };
22
+ /** Where a set is used, in words: `Home: hero, Footer: footer`. */
23
+ const placesText = (usages) => usages.slice(0, 8).map(u => `${u.file}${u.id ? ` (${u.id})` : ` line ${u.line}`}`).join(', ') + (usages.length > 8 ? `, and ${usages.length - 8} more` : '');
24
+ export const stylesetList = defineOperation({
25
+ name: 'styleset-list',
26
+ readOnly: true,
27
+ summary: `The shared styles of the site: each a name (the class is ${STYLESET_PREFIX}name), the Tailwind classes it holds, what that comes to in words, and where it is used. An element uses one by having its class: change the style and every element that uses it changes.`,
28
+ run: (_input, ctx) => ({ prefix: STYLESET_PREFIX, sets: readStyleSets(ctx).map(set => describe(ctx, set)) }),
29
+ });
30
+ export const stylesetCreate = defineOperation({
31
+ name: 'styleset-create',
32
+ summary: 'Makes a shared style. With id: from that element\'s classes, which move into the style and the element then uses it (apply: false leaves the element as it is); classes of the element that Tailwind cannot apply (its own, other shared styles) stay on it. Without id: from `classes` (a text of Tailwind classes), or empty. The name is lowercase words (card, hero-title); its class is s-<name>.',
33
+ params: [
34
+ ...ELEMENT_PARAMS,
35
+ { name: 'name', type: 'string', required: true, help: 'what the style is called: lowercase words and numbers joined by dashes (card, hero-title)' },
36
+ { name: 'classes', type: 'string', help: 'without id: the Tailwind classes it holds (all must be ones Tailwind knows)' },
37
+ { name: 'apply', type: 'boolean', default: true, help: 'with id: the element uses the new style (default), or keeps its classes as they are' },
38
+ ],
39
+ run: async (input, ctx) => {
40
+ const name = requiredText(input, 'name');
41
+ checkStyleSetName(name);
42
+ if (readStyleSets(ctx).some(s => s.name === name))
43
+ throw new OperationError(409, 'styleset-exists', `there is already a style called "${name}"`);
44
+ const { tokens: colorTokens } = themeColorsOf(ctx);
45
+ const sets = new Set(readStyleSets(ctx).map(s => s.class));
46
+ const fits = async (classes) => {
47
+ const candidates = classes.filter(c => !sets.has(c));
48
+ const unknown = await unknownUtilities(ctx, candidates, colorTokens);
49
+ // Without Tailwind to ask, only what the style table reads is trusted
50
+ const known = unknown === null
51
+ ? new Set(candidates.filter(c => !wrStyleUnmanaged(c, optionsOf(ctx)).includes(c)))
52
+ : new Set(candidates.filter(c => !unknown.includes(c)));
53
+ return { fit: classes.filter(c => known.has(c)), kept: classes.filter(c => !known.has(c)) };
54
+ };
55
+ if (text(input, 'id') === undefined) {
56
+ const classes = wrParseClassName(text(input, 'classes') ?? '').map(t => t.raw);
57
+ const { fit, kept } = await fits(classes);
58
+ if (kept.length > 0)
59
+ throw invalid(`Tailwind cannot apply: ${kept.join(', ')}. A style holds only classes Tailwind knows (not the project's own classes, not other shared styles)`, 'unknown-classes');
60
+ writeStyleSet(ctx, name, fit);
61
+ return { created: describe(ctx, readStyleSet(ctx, name)), changed: false };
62
+ }
63
+ const { found } = element(ctx, input);
64
+ const classes = classesOf(found);
65
+ if (classes.className === null)
66
+ throw new OperationError(409, 'class-not-editable', classes.reason);
67
+ const { fit, kept } = await fits(wrParseClassName(classes.className).map(t => t.raw));
68
+ writeStyleSet(ctx, name, fit);
69
+ const apply = flag(input, 'apply') ?? true;
70
+ const result = apply ? changeClasses(ctx, input, () => [styleSetClass(name), ...kept].join(' ')) : changeClasses(ctx, input, className => className);
71
+ return { ...result, created: describe(ctx, readStyleSet(ctx, name)), kept };
72
+ },
73
+ });
74
+ export const stylesetApply = defineOperation({
75
+ name: 'styleset-apply',
76
+ summary: 'Puts a shared style on an element: everything the element had in its classes is replaced by the style\'s class (the element then looks like the style and changes with it).',
77
+ params: [...ELEMENT_PARAMS, { name: 'name', type: 'string', required: true, help: 'the shared style (see styleset-list)' }],
78
+ run: (input, ctx) => {
79
+ const set = readStyleSet(ctx, requiredText(input, 'name'));
80
+ return { ...changeClasses(ctx, input, () => set.class), applied: set.name };
81
+ },
82
+ });
83
+ /** What a class text becomes when the shared styles it names are written out: each stands for what it holds, under the classes the element has of its own. */
84
+ function writtenOut(className, sets, options) {
85
+ const names = new Set(sets.map(s => s.class));
86
+ const own = wrParseClassName(className).map(t => t.raw).filter(c => !names.has(c)).join(' ');
87
+ return sets.reduce((out, set) => wrStyleLayer(set.classes, out, options), own);
88
+ }
89
+ export const stylesetDetach = defineOperation({
90
+ name: 'styleset-detach',
91
+ summary: 'Takes a shared style off an element, keeping how it looks: the style\'s classes are written on the element (where the element has classes of its own for the same thing, its own win). name: only that style (default: every one the element uses).',
92
+ params: [...ELEMENT_PARAMS, { name: 'name', type: 'string', help: 'which shared style to take off (default: all the element uses)' }],
93
+ run: (input, ctx) => {
94
+ const { found } = element(ctx, input);
95
+ const classes = classesOf(found);
96
+ if (classes.className === null)
97
+ throw new OperationError(409, 'class-not-editable', classes.reason);
98
+ const wanted = text(input, 'name');
99
+ const used = setsOf(ctx, classes.className).map(s => readStyleSet(ctx, s.name)).filter(s => wanted === undefined || s.name === wanted);
100
+ const options = optionsOf(ctx);
101
+ return changeClasses(ctx, input, className => (used.length === 0 ? className : writtenOut(className, used, options)));
102
+ },
103
+ });
104
+ export const stylesetDetachAll = defineOperation({
105
+ name: 'styleset-detach-all',
106
+ summary: 'Takes a shared style off every element that uses it, keeping how each looks (the style\'s classes are written on each, under the classes the element has of its own). Afterwards the style can be deleted. Not undoable from the editor: it touches many files.',
107
+ params: [{ name: 'name', type: 'string', positional: true, required: true, help: 'the shared style (see styleset-list)' }],
108
+ run: (input, ctx) => {
109
+ const set = readStyleSet(ctx, requiredText(input, 'name'));
110
+ const options = optionsOf(ctx);
111
+ const detached = replaceStyleSetUsages(ctx, set.class, others => wrStyleLayer(set.classes, others, options));
112
+ return { name: set.name, detached };
113
+ },
114
+ });
115
+ export const stylesetRename = defineOperation({
116
+ name: 'styleset-rename',
117
+ summary: 'Gives a shared style another name: its rule in theme.css and the class in every element that uses it change together (each keeps its place among the element\'s other classes). The new name is lowercase words (hero-card); it cannot be one another style has.',
118
+ params: [
119
+ { name: 'name', type: 'string', positional: true, required: true, help: 'the shared style (see styleset-list)' },
120
+ { name: 'to', type: 'string', required: true, help: 'the new name' },
121
+ ],
122
+ run: (input, ctx) => {
123
+ const from = requiredText(input, 'name');
124
+ const to = requiredText(input, 'to');
125
+ const { places } = renameStyleSet(ctx, from, to);
126
+ return { renamed: from !== to, from, to, places, set: describe(ctx, readStyleSet(ctx, to)) };
127
+ },
128
+ });
129
+ export const stylesetDelete = defineOperation({
130
+ name: 'styleset-delete',
131
+ summary: 'Deletes a shared style. Refused while any element uses it: it says where, and what to do (styleset-detach-all, or detach it from those elements).',
132
+ destructive: true,
133
+ params: [{ name: 'name', type: 'string', positional: true, required: true, help: 'the shared style (see styleset-list)' }],
134
+ run: (input, ctx) => {
135
+ const set = readStyleSet(ctx, requiredText(input, 'name'));
136
+ const usages = findStyleSetUsages(ctx, set.class).map(u => u.usage);
137
+ if (usages.length > 0) {
138
+ throw new OperationError(409, 'styleset-in-use', `"${set.name}" is used in ${usages.length} ${usages.length === 1 ? 'place' : 'places'} (${placesText(usages)}). To delete it, detach it from all of them first (styleset-detach-all), and then delete it.`);
139
+ }
140
+ removeStyleSet(ctx, set.name);
141
+ return { deleted: set.name };
142
+ },
143
+ });
144
+ export const STYLESET_OPERATIONS = [stylesetList, stylesetCreate, stylesetApply, stylesetDetach, stylesetDetachAll, stylesetRename, stylesetDelete];
145
+ //# sourceMappingURL=stylesets.js.map
@@ -0,0 +1,6 @@
1
+ export declare const themeGet: import("..").Operation;
2
+ export declare const themeSet: import("..").Operation;
3
+ export declare const themeRemove: import("..").Operation;
4
+ export declare const themeDark: import("..").Operation;
5
+ export declare const THEME_OPERATIONS: import("..").Operation[];
6
+ //# sourceMappingURL=theme.d.ts.map