mdeck 3.0.0 → 3.1.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.
- package/CHANGELOG.md +11 -1
- package/assets/{packs/lagoon → extensions/palettes}/lagoon/extension.toml +3 -0
- package/assets/{packs/aurora → extensions/palettes}/neon/extension.toml +3 -0
- package/assets/{packs/academic → extensions/palettes}/nordic/extension.toml +3 -0
- package/assets/{packs/minimal → extensions/palettes}/paper/extension.toml +3 -0
- package/assets/{packs/neue → extensions/palettes}/swiss/extension.toml +3 -0
- package/assets/{packs/academic → extensions/themes}/academic/extension.toml +3 -0
- package/assets/{packs/aurora → extensions/themes}/aurora/extension.toml +3 -0
- package/assets/{packs/minimal → extensions/themes}/minimal/extension.toml +3 -0
- package/assets/{packs/neue → extensions/themes}/neue/extension.toml +3 -0
- package/docs/reference/authoring.md +3 -3
- package/docs/reference/extensions.md +40 -35
- package/docs/reference/palettes.md +1 -1
- package/docs/reference/themes.md +3 -3
- package/docs/site/content/appearance.md +11 -9
- package/docs/site/content/commands.md +2 -1
- package/docs/site/content/theme-authoring.md +1 -1
- package/docs/site/content/theme-repository.md +36 -28
- package/package.json +2 -2
- package/skills/write-slides/SKILL.md +1 -1
- package/src/build/editorPlugin.js +22 -17
- package/src/build/themeCheck.js +2 -1
- package/src/cli/args.js +2 -1
- package/src/cli/index.js +7 -6
- package/src/cli/themes.js +92 -91
- package/src/editor/api.js +11 -10
- package/src/editor/components/DesignApp.jsx +9 -11
- package/src/editor/components/ExtensionEditor.jsx +10 -11
- package/src/editor/components/ExtensionsPanel.jsx +24 -28
- package/src/extensions/discover.js +28 -35
- package/src/extensions/installed.js +73 -48
- package/src/extensions/manifest.js +20 -3
- package/src/extensions/moved.js +14 -7
- package/src/extensions/packages.js +318 -0
- package/src/paths.js +0 -2
- package/assets/packs/academic/pack.toml +0 -8
- package/assets/packs/aurora/pack.toml +0 -8
- package/assets/packs/lagoon/pack.toml +0 -8
- package/assets/packs/minimal/pack.toml +0 -8
- package/assets/packs/neue/pack.toml +0 -8
- package/src/extensions/packs.js +0 -357
- /package/assets/{packs/academic → extensions/themes}/academic/styles.css +0 -0
- /package/assets/{packs/aurora → extensions/themes}/aurora/styles.css +0 -0
- /package/assets/{packs/minimal → extensions/themes}/minimal/styles.css +0 -0
- /package/assets/{packs/neue → extensions/themes}/neue/styles.css +0 -0
|
@@ -5,7 +5,7 @@ import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSy
|
|
|
5
5
|
import { resolve, dirname, relative } from 'node:path'
|
|
6
6
|
import { homedir } from 'node:os'
|
|
7
7
|
import { MANIFEST_FILENAME, ManifestError, KINDS, parseManifestText, validateManifest } from './manifest.js'
|
|
8
|
-
import { builtinExtensionsRoot
|
|
8
|
+
import { builtinExtensionsRoot } from '../paths.js'
|
|
9
9
|
|
|
10
10
|
const SKIP = name => name.startsWith('.') || name === 'node_modules'
|
|
11
11
|
|
|
@@ -20,36 +20,31 @@ export function userExtensionsDir() {
|
|
|
20
20
|
return resolve(mdeckHome(), 'extensions')
|
|
21
21
|
}
|
|
22
22
|
|
|
23
|
-
// The
|
|
24
|
-
// they stay in mdeck's folder but are not offered.
|
|
23
|
+
// The built-in themes and palettes this user removed (`mdeck themes remove
|
|
24
|
+
// aurora`): they stay in mdeck's folder but are not offered. Kept as
|
|
25
|
+
// "theme:aurora" and "palette:neon".
|
|
25
26
|
const removedFile = () => resolve(mdeckHome(), 'removed.json')
|
|
26
|
-
export function
|
|
27
|
-
|
|
27
|
+
export function removedBuiltIns() {
|
|
28
|
+
let saved = {}
|
|
29
|
+
try { saved = JSON.parse(readFileSync(removedFile(), 'utf8')) } catch {}
|
|
30
|
+
return (Array.isArray(saved.hidden) ? saved.hidden : []).filter(key => /^(theme|palette):[a-z][a-z0-9-]*$/.test(key))
|
|
28
31
|
}
|
|
29
|
-
export function
|
|
32
|
+
export function setRemovedBuiltIns(keys) {
|
|
30
33
|
mkdirSync(mdeckHome(), { recursive: true })
|
|
31
|
-
writeFileSync(removedFile(), JSON.stringify({
|
|
34
|
+
writeFileSync(removedFile(), JSON.stringify({ hidden: [...new Set(keys)].sort() }, null, 2) + '\n')
|
|
32
35
|
}
|
|
33
36
|
|
|
34
|
-
//
|
|
35
|
-
// (not `hidden`, by default those this user removed).
|
|
36
|
-
export function bundledPacks({ root = bundledPacksRoot, all = false, hidden = all ? [] : removedPacks() } = {}) {
|
|
37
|
-
if (!existsSync(root)) return {}
|
|
38
|
-
const removed = hidden
|
|
39
|
-
return Object.fromEntries(sortedEntries(root).map(entry => entry.name).filter(id => !removed.includes(id)).map(id => [id, resolve(root, id)]))
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
// Built-in layouts and the bundled packs, then the user's, then the deck's
|
|
37
|
+
// mdeck's own layouts, themes and palettes, then the user's, then the deck's
|
|
43
38
|
// own. For themes and palettes a later place replaces an earlier one with the
|
|
44
|
-
// same id: an installed newer version replaces the
|
|
45
|
-
// copy replaces both.
|
|
46
|
-
// `userRoot: null` leaves out what this user installed and
|
|
47
|
-
// results that must not depend on the computer (checks, the
|
|
48
|
-
|
|
39
|
+
// same id: an installed newer version replaces the built-in one, and a
|
|
40
|
+
// deck's copy replaces both. Built-in themes and palettes this user removed
|
|
41
|
+
// are left out. `userRoot: null` leaves out what this user installed and
|
|
42
|
+
// removed, for results that must not depend on the computer (checks, the
|
|
43
|
+
// repository).
|
|
44
|
+
export function extensionRoots(slidesPath, { builtinRoot = builtinExtensionsRoot, userRoot = userExtensionsDir() } = {}) {
|
|
49
45
|
const deckDir = dirname(resolve(slidesPath))
|
|
50
46
|
return [
|
|
51
|
-
{ dir: builtinRoot, source: 'built-in' },
|
|
52
|
-
...Object.entries(bundledPacks({ root: packsRoot, hidden: userRoot ? removedPacks() : [] })).map(([pack, dir]) => ({ dir, source: 'built-in', pack })),
|
|
47
|
+
{ dir: builtinRoot, source: 'built-in', hidden: userRoot ? removedBuiltIns() : [] },
|
|
53
48
|
...(userRoot ? [{ dir: userRoot, source: 'user' }] : []),
|
|
54
49
|
{ dir: resolve(deckDir, 'extensions'), source: 'local' },
|
|
55
50
|
]
|
|
@@ -80,21 +75,17 @@ function readLayout(record) {
|
|
|
80
75
|
function loadManifest({ dir, file, folderName }) {
|
|
81
76
|
const raw = parseManifestText(readFileSync(file, 'utf8'), file)
|
|
82
77
|
const record = validateManifest(raw, { file, dir, folderName })
|
|
83
|
-
|
|
84
|
-
let pack = null
|
|
85
|
-
try { pack = JSON.parse(readFileSync(resolve(dir, '.mdeck-pack.json'), 'utf8')).pack ?? null } catch {}
|
|
86
|
-
const withPack = pack ? { ...record, pack } : record
|
|
87
|
-
return record.kind === 'layout' ? readLayout(withPack) : withPack
|
|
78
|
+
return record.kind === 'layout' ? readLayout(record) : record
|
|
88
79
|
}
|
|
89
80
|
|
|
90
81
|
export function discoverExtensions(roots) {
|
|
91
82
|
const registry = { layouts: {}, themes: {}, palettes: {}, records: [], warnings: [] }
|
|
92
83
|
const byKey = new Map()
|
|
93
84
|
const RANK = { 'built-in': 0, user: 1, local: 2 }
|
|
94
|
-
const add = (record, source
|
|
85
|
+
const add = (record, source) => {
|
|
95
86
|
const key = `${record.kind}:${record.id}`
|
|
96
87
|
const existing = byKey.get(key)
|
|
97
|
-
const entry = { ...record, source
|
|
88
|
+
const entry = { ...record, source }
|
|
98
89
|
// Themes and palettes may be replaced from a later place; layouts and two
|
|
99
90
|
// with one id in the same place may not.
|
|
100
91
|
const replaces = existing && record.kind !== 'layout' && RANK[source] > RANK[existing.source]
|
|
@@ -108,7 +99,10 @@ export function discoverExtensions(roots) {
|
|
|
108
99
|
}
|
|
109
100
|
for (const root of roots) {
|
|
110
101
|
if (!existsSync(root.dir) || !statSync(root.dir).isDirectory()) continue
|
|
111
|
-
for (const found of walk(root.dir)) {
|
|
102
|
+
for (const found of walk(root.dir)) {
|
|
103
|
+
const record = loadManifest(found)
|
|
104
|
+
if (!root.hidden?.includes(`${record.kind}:${record.id}`)) add(record, root.source)
|
|
105
|
+
}
|
|
112
106
|
}
|
|
113
107
|
// Themes and palettes in name order, wherever they come from.
|
|
114
108
|
for (const kind of ['themes', 'palettes']) registry[kind] = Object.fromEntries(Object.entries(registry[kind]).sort(([a], [b]) => a.localeCompare(b, 'en')))
|
|
@@ -135,10 +129,9 @@ function checkPaletteReferences(registry) {
|
|
|
135
129
|
}
|
|
136
130
|
}
|
|
137
131
|
|
|
138
|
-
// The extensions that come with mdeck, alone
|
|
139
|
-
// removed or not.
|
|
132
|
+
// The extensions that come with mdeck, alone, removed or not.
|
|
140
133
|
export function builtInRegistry() {
|
|
141
|
-
return discoverExtensions([{ dir: builtinExtensionsRoot, source: 'built-in' }
|
|
134
|
+
return discoverExtensions([{ dir: builtinExtensionsRoot, source: 'built-in' }])
|
|
142
135
|
}
|
|
143
136
|
|
|
144
137
|
export function loadRegistry(slidesPath, options) {
|
|
@@ -158,7 +151,7 @@ export function serializeRegistry(registry, { relativeTo } = {}) {
|
|
|
158
151
|
const out = { schema: 2, warnings: registry.warnings }
|
|
159
152
|
for (const kind of KINDS) {
|
|
160
153
|
out[`${kind}s`] = Object.values(registry[`${kind}s`]).map(record => ({
|
|
161
|
-
...record.manifest, kind, source: record.source, ...(record.
|
|
154
|
+
...record.manifest, kind, source: record.source, ...(record.package?.version ? { version: record.package.version } : {}),
|
|
162
155
|
file: relativeTo ? relative(relativeTo, record.file) : record.file,
|
|
163
156
|
}))
|
|
164
157
|
}
|
|
@@ -1,63 +1,88 @@
|
|
|
1
|
-
// Where
|
|
2
|
-
//
|
|
3
|
-
// and the design page.
|
|
4
|
-
import { existsSync, statSync } from 'node:fs'
|
|
1
|
+
// Where themes and palettes are installed: for every deck (the user's
|
|
2
|
+
// folder, where the built-in ones are offered too) or beside one deck.
|
|
3
|
+
// Shared by mdeck themes / mdeck palettes and the design page.
|
|
4
|
+
import { existsSync, rmSync, statSync } from 'node:fs'
|
|
5
5
|
import { dirname, resolve } from 'node:path'
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
6
|
+
import { loadRegistry, mdeckHome, removedBuiltIns, setRemovedBuiltIns, userExtensionsDir } from './discover.js'
|
|
7
|
+
import { installedPackages, installPackage, keyOf, obtainPackage, PackageError, removePackage, satisfies, withDependencies } from './packages.js'
|
|
8
|
+
|
|
9
|
+
const fail = message => { throw new PackageError(message) }
|
|
8
10
|
|
|
9
11
|
// Where a command works: for every deck (the user's folder), or beside a
|
|
10
12
|
// deck when one is named or --local is given.
|
|
11
13
|
export function target(where, local) {
|
|
12
|
-
if (!where && !local) return { user: true, root: userExtensionsDir(), deck: resolve('slides.md'), label: 'for every deck' }
|
|
14
|
+
if (!where && !local) return { user: true, root: userExtensionsDir(), deck: resolve(mdeckHome(), 'no-deck', 'slides.md'), label: 'for every deck' }
|
|
13
15
|
const given = resolve(where ?? '.')
|
|
14
|
-
if (!existsSync(given) && !/\.md$/i.test(given))
|
|
16
|
+
if (!existsSync(given) && !/\.md$/i.test(given)) fail(`${where} does not exist; name a slides file or the folder it is in`)
|
|
15
17
|
const folder = existsSync(given) && statSync(given).isDirectory() ? given : dirname(given)
|
|
16
18
|
return { user: false, root: resolve(folder, 'extensions'), deck: existsSync(given) && !statSync(given).isDirectory() ? given : resolve(folder, 'slides.md'), label: `in ${resolve(folder, 'extensions')}` }
|
|
17
19
|
}
|
|
18
20
|
|
|
19
|
-
// The
|
|
20
|
-
//
|
|
21
|
-
|
|
22
|
-
const installed = installedPacks(where.root)
|
|
23
|
-
if (!where.user) return installed
|
|
24
|
-
const offered = Object.fromEntries(Object.keys(bundledPacks()).map(id => [id, { bundled: true, requires: [] }]))
|
|
25
|
-
return { ...offered, ...installed }
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// The packs at `where` that require `id`.
|
|
29
|
-
export function requiredBy(id, where, catalogue) {
|
|
30
|
-
return Object.entries(packsAt(where)).filter(([other, pack]) => {
|
|
31
|
-
if (other === id) return false
|
|
32
|
-
const requires = pack.bundled ? catalogue.packs.find(p => p.id === other)?.requires ?? [] : pack.requires
|
|
33
|
-
return requires.includes(id)
|
|
34
|
-
}).map(([other]) => other)
|
|
35
|
-
}
|
|
21
|
+
// The themes and palettes `where` sees, as a registry: for every deck what
|
|
22
|
+
// this user has, beside a deck also what that deck has.
|
|
23
|
+
const registryAt = where => loadRegistry(where.deck)
|
|
36
24
|
|
|
37
|
-
// Installs a
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const
|
|
44
|
-
|
|
25
|
+
// Installs a theme or palette with what it needs. What `where` has already
|
|
26
|
+
// stays as it is. A built-in one is not copied: for every deck it is offered
|
|
27
|
+
// again if it was removed. A theme and its own palette may need each other,
|
|
28
|
+
// so the registry is checked before anything is written and once all is
|
|
29
|
+
// there; if anything fails, what this call installed is taken out again.
|
|
30
|
+
export async function installHere(catalogue, kind, id, where, { mdeckVersion, force = false }) {
|
|
31
|
+
const before = registryAt(where)
|
|
32
|
+
const plan = withDependencies(catalogue, kind, id)
|
|
33
|
+
const done = [], written = []
|
|
34
|
+
try {
|
|
35
|
+
for (const entry of plan) {
|
|
36
|
+
const key = keyOf(entry.kind, entry.id)
|
|
37
|
+
const requested = entry.kind === kind && entry.id === id
|
|
38
|
+
if (entry.builtIn) {
|
|
39
|
+
const hidden = removedBuiltIns()
|
|
40
|
+
const wasRemoved = hidden.includes(key)
|
|
41
|
+
if (wasRemoved) setRemovedBuiltIns(hidden.filter(other => other !== key))
|
|
42
|
+
done.push({ kind: entry.kind, id: entry.id, version: entry.version, builtIn: true, wasRemoved, requested })
|
|
43
|
+
continue
|
|
44
|
+
}
|
|
45
|
+
const have = installedPackages(where.root)[key]
|
|
46
|
+
// Beside a deck, what is installed only for every deck is copied too,
|
|
47
|
+
// so the slide folder carries it.
|
|
48
|
+
const there = before[`${entry.kind}s`][entry.id]
|
|
49
|
+
if (!requested && (have || (there && (where.user || there.source !== 'user')))) { done.push({ kind: entry.kind, id: entry.id, version: have?.version ?? null, kept: true, requested }); continue }
|
|
50
|
+
if (!satisfies(entry.mdeck, mdeckVersion)) fail(`The ${entry.kind} ${entry.id} ${entry.version} needs mdeck ${entry.mdeck}; this is ${mdeckVersion}. Update mdeck first: npm install -g mdeck`)
|
|
51
|
+
const { bundle, url, digest } = await obtainPackage(catalogue, entry)
|
|
52
|
+
const result = installPackage(bundle, { root: where.root, url, digest, force })
|
|
53
|
+
if (!result.replaced) written.push(result.dir)
|
|
54
|
+
done.push({ kind: entry.kind, id: entry.id, version: entry.version, replaced: result.replaced, requested })
|
|
55
|
+
}
|
|
56
|
+
registryAt(where)
|
|
57
|
+
} catch (error) {
|
|
58
|
+
for (const dir of written) rmSync(dir, { recursive: true, force: true })
|
|
59
|
+
throw error
|
|
60
|
+
}
|
|
61
|
+
return done
|
|
45
62
|
}
|
|
46
63
|
|
|
47
|
-
// Removes a
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
const
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (where.user) {
|
|
57
|
-
|
|
58
|
-
|
|
64
|
+
// Removes a theme or palette. A built-in one is hidden instead, for every
|
|
65
|
+
// deck; installing it brings it back. A palette a theme here uses by default
|
|
66
|
+
// stays; a theme takes the palettes that belong only to it along; the last
|
|
67
|
+
// theme stays.
|
|
68
|
+
export function removeHere(kind, id, where, { force = false } = {}) {
|
|
69
|
+
const registry = registryAt(where)
|
|
70
|
+
const record = registry[`${kind}s`][id]
|
|
71
|
+
const installed = installedPackages(where.root)
|
|
72
|
+
const key = keyOf(kind, id)
|
|
73
|
+
if (!installed[key] && !(where.user && record?.source === 'built-in')) fail(`The ${kind} ${id} is not installed ${where.label}`)
|
|
74
|
+
if (kind === 'palette') {
|
|
75
|
+
const users = Object.values(registry.themes).filter(theme => theme.manifest.palette === id).map(theme => theme.id)
|
|
76
|
+
if (users.length) fail(`${users.join(', ')} ${users.length > 1 ? 'use' : 'uses'} the palette ${id} by default; remove ${users.length > 1 ? 'those themes' : 'that theme'} first`)
|
|
77
|
+
}
|
|
78
|
+
if (kind === 'theme' && Object.keys(registry.themes).length === 1) fail(`${id} is the last theme ${where.label}; install another theme first (mdeck themes search lists them)`)
|
|
79
|
+
const removed = []
|
|
80
|
+
const take = (k, i) => {
|
|
81
|
+
const r = registry[`${k}s`][i]
|
|
82
|
+
if (installedPackages(where.root)[keyOf(k, i)]) { removePackage(k, i, { root: where.root, force }); removed.push({ kind: k, id: i }) }
|
|
83
|
+
else if (where.user && r?.source === 'built-in') { setRemovedBuiltIns([...removedBuiltIns(), keyOf(k, i)]); removed.push({ kind: k, id: i, hidden: true }) }
|
|
59
84
|
}
|
|
60
|
-
|
|
61
|
-
if (
|
|
62
|
-
return
|
|
85
|
+
take(kind, id)
|
|
86
|
+
if (kind === 'theme') for (const palette of Object.values(registry.palettes)) if (palette.manifest.theme === id) take('palette', palette.id)
|
|
87
|
+
return removed
|
|
63
88
|
}
|
|
@@ -15,11 +15,16 @@ export const MANIFEST_FILENAME = 'extension.toml'
|
|
|
15
15
|
const FRAMES = ['standard', 'title', 'chapter', 'none']
|
|
16
16
|
const PROPERTY_TYPES = ['string', 'number', 'integer', 'boolean', 'array', 'object']
|
|
17
17
|
const SHARED_KEYS = ['schema', 'kind', 'id', 'title', 'description']
|
|
18
|
+
// A theme or palette is also a package that can be installed: its version,
|
|
19
|
+
// who made it, its licence, a web page and the oldest mdeck it works with.
|
|
20
|
+
const PACKAGE_KEYS = ['version', 'author', 'license', 'homepage', 'mdeck']
|
|
18
21
|
const KEYS = {
|
|
19
22
|
layout: [...SHARED_KEYS, 'frame', 'files', 'regions', 'properties'],
|
|
20
|
-
theme: [...SHARED_KEYS, 'palette', 'palettes', 'appearance', 'fonts', 'files', 'tokens', 'params'],
|
|
21
|
-
palette: [...SHARED_KEYS, 'theme', 'light', 'dark'],
|
|
23
|
+
theme: [...SHARED_KEYS, ...PACKAGE_KEYS, 'palette', 'palettes', 'appearance', 'fonts', 'files', 'tokens', 'params'],
|
|
24
|
+
palette: [...SHARED_KEYS, ...PACKAGE_KEYS, 'theme', 'light', 'dark'],
|
|
22
25
|
}
|
|
26
|
+
export const VERSION_RE = /^\d+\.\d+\.\d+$/
|
|
27
|
+
export const RANGE_RE = /^>=\s*(\d+\.\d+\.\d+)$/
|
|
23
28
|
const SCHEMA_KEYS = ['type', 'title', 'description', 'enum', 'required', 'default', 'minimum', 'maximum', 'minItems', 'maxItems', 'items']
|
|
24
29
|
const RESERVED_NAMES = ['constructor', 'prototype', '__proto__']
|
|
25
30
|
|
|
@@ -72,7 +77,19 @@ export function validateManifest(raw, { file, dir, folderName, fileExists = exis
|
|
|
72
77
|
const manifest = kind === 'layout' ? validateLayout(raw, shared, fail)
|
|
73
78
|
: kind === 'theme' ? validateTheme(raw, shared, fail)
|
|
74
79
|
: validatePalette(raw, shared, fail)
|
|
75
|
-
|
|
80
|
+
const pkg = kind === 'layout' ? null : validatePackage(raw, fail)
|
|
81
|
+
return { kind, id: raw.id, title: shared.title, description: shared.description, dir, file, manifest, files, ...(pkg ? { package: pkg } : {}) }
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// The package settings, all optional for a theme or palette of one's own;
|
|
85
|
+
// the theme repository requires version, author and license.
|
|
86
|
+
function validatePackage(raw, fail) {
|
|
87
|
+
const pkg = {}
|
|
88
|
+
if (raw.version != null) { if (typeof raw.version !== 'string' || !VERSION_RE.test(raw.version)) fail('version must look like 1.0.0', 'version'); pkg.version = raw.version }
|
|
89
|
+
for (const key of ['author', 'license']) if (raw[key] != null) { if (typeof raw[key] !== 'string' || !raw[key].trim()) fail(`${key} must be text`, key); pkg[key] = raw[key].trim() }
|
|
90
|
+
if (raw.homepage != null) { if (typeof raw.homepage !== 'string' || !/^https:\/\//.test(raw.homepage)) fail('homepage must be an https:// address', 'homepage'); pkg.homepage = raw.homepage }
|
|
91
|
+
if (raw.mdeck != null) { if (typeof raw.mdeck !== 'string' || !RANGE_RE.test(raw.mdeck)) fail('mdeck must look like ">=3.1.0"', 'mdeck'); pkg.mdeck = raw.mdeck }
|
|
92
|
+
return pkg
|
|
76
93
|
}
|
|
77
94
|
|
|
78
95
|
const FILE_KEYS = { layout: ['layout', 'styles', 'starter'], theme: ['styles'], palette: [] }
|
package/src/extensions/moved.js
CHANGED
|
@@ -1,13 +1,20 @@
|
|
|
1
1
|
// Themes and palettes that came with mdeck before 3.0 and now live in the
|
|
2
|
-
// theme repository
|
|
3
|
-
//
|
|
2
|
+
// theme repository. A deck that names one is told how to install it instead
|
|
3
|
+
// of being told the name is unknown.
|
|
4
4
|
export const MOVED = {
|
|
5
|
-
themes:
|
|
6
|
-
palettes:
|
|
5
|
+
themes: ['duet', 'editorial', 'fhnw', 'terminal', 'sketch'],
|
|
6
|
+
palettes: ['cobalt', 'terra', 'brand', 'phosphor', 'pastel', 'forest'],
|
|
7
7
|
}
|
|
8
8
|
|
|
9
|
-
//
|
|
9
|
+
// Palettes that were removed because another one looks almost the same, by
|
|
10
|
+
// the one to use instead.
|
|
11
|
+
export const REPLACED = { palettes: { graphite: 'nordic', ember: 'paper' } }
|
|
12
|
+
|
|
13
|
+
// "The theme "duet" is no longer built into mdeck; install it with: mdeck
|
|
14
|
+
// themes install duet", what to use instead of a removed one, or null.
|
|
15
|
+
// Installing goes to every deck by default.
|
|
10
16
|
export function movedHint(kind, id) {
|
|
11
|
-
const
|
|
12
|
-
|
|
17
|
+
const instead = REPLACED[`${kind}s`]?.[id]
|
|
18
|
+
if (instead) return `The ${kind} "${id}" was removed from mdeck; use ${kind}: ${instead}, which looks almost the same`
|
|
19
|
+
return MOVED[`${kind}s`]?.includes(id) ? `The ${kind} "${id}" is no longer built into mdeck; install it with: mdeck ${kind}s install ${id}` : null
|
|
13
20
|
}
|