@inizioevoke/veeva-astroclm-core 1.4.2 → 1.4.4
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/agents/claude/evoke-page-manager/SKILL.md +17 -5
- package/dist/apps/page-manager/bulk-create.d.ts +1 -1
- package/dist/apps/page-manager/bulk-create.js +16 -3
- package/dist/apps/page-manager/index.d.ts +1 -1
- package/dist/apps/page-manager/index.js +34 -4
- package/dist/apps/page-manager/single-create.d.ts +1 -1
- package/dist/apps/page-manager/single-create.js +3 -1
- package/dist/apps/page-manager/utils.d.ts +4 -1
- package/dist/apps/page-manager/utils.js +56 -0
- package/inizioevoke-veeva-astroclm-core-1.4.4.tgz +0 -0
- package/package.json +1 -1
- package/src/apps/page-manager/bulk-create.ts +17 -5
- package/src/apps/page-manager/index.ts +37 -6
- package/src/apps/page-manager/single-create.ts +7 -3
- package/src/apps/page-manager/utils.ts +69 -1
- package/src/components/scripts/ResizeScript/ResizeScript.astro +7 -4
- package/src/components/scripts/ResizeScript/resize.ts +24 -11
|
@@ -32,9 +32,9 @@ Check if `veeva-config.ts` exists in the project root. If it does, ask the user:
|
|
|
32
32
|
|
|
33
33
|
## Step 4 — Create files
|
|
34
34
|
|
|
35
|
-
Given the page path (e.g. `
|
|
36
|
-
- `contentName` = last segment of the path (e.g. `
|
|
37
|
-
- `pageName` = full path with `/` replaced by `-` (e.g. `this-one`)
|
|
35
|
+
Given the page path (e.g. `This/One`):
|
|
36
|
+
- `contentName` = last segment of the path, preserving its original case (e.g. `One`)
|
|
37
|
+
- `pageName` = full path lowercased with `/` replaced by `-` (e.g. `this-one`)
|
|
38
38
|
|
|
39
39
|
Check that neither `src/pages/<pageName>.astro` nor `src/contents/<path>/` already exist — if either does, stop and tell the user.
|
|
40
40
|
|
|
@@ -70,6 +70,18 @@ import './<contentName>.scss';
|
|
|
70
70
|
|
|
71
71
|
If the user said yes in Step 3, add a new slide entry to `presentation.slides` following the pattern in `../veeva-config-manager/manage.md` (Step 3 — Resolve missing slides).
|
|
72
72
|
|
|
73
|
-
## Step 6 —
|
|
73
|
+
## Step 6 — Update Pages array in src/data/pages.tsx
|
|
74
74
|
|
|
75
|
-
|
|
75
|
+
For each page created, build its URL as `/<pageName>/` (must start and end with `/`). Collect these into a `string[]`.
|
|
76
|
+
|
|
77
|
+
Ask the user: "Would you like me to automatically append these URLs to the `Pages` array in `src/data/pages.tsx`?"
|
|
78
|
+
|
|
79
|
+
- **If yes:** Edit `src/data/pages.tsx` and append an entry to the `Pages` array for each URL, following the existing `Page` interface shape (`{ title: JSX.Element, href: string }`). The title is the `pageName` split on `-` with each segment capitalized and joined by spaces, e.g. for `pageName` `module-1-slide-2`:
|
|
80
|
+
```tsx
|
|
81
|
+
{ title: <>Module 1 Slide 2</>, href: '/module-1-slide-2/' }
|
|
82
|
+
```
|
|
83
|
+
- **If no:** Log the URLs to the console so the user can copy them manually.
|
|
84
|
+
|
|
85
|
+
## Step 7 — Report
|
|
86
|
+
|
|
87
|
+
Tell the user which files were created and whether `src/data/pages.tsx` was updated (or print the URL list if it wasn't).
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export default function bulkCreate(rootDir: string): Promise<
|
|
1
|
+
export default function bulkCreate(rootDir: string): Promise<string[]>;
|
|
@@ -2,7 +2,7 @@ import { existsSync } from 'node:fs';
|
|
|
2
2
|
import { readFile, rm } from 'node:fs/promises';
|
|
3
3
|
import { join, resolve as resolvePath } from 'node:path';
|
|
4
4
|
import { parse } from 'yaml';
|
|
5
|
-
import { text, select, spinner,
|
|
5
|
+
import { text, select, spinner, box } from '@clack/prompts';
|
|
6
6
|
import { sleep, handleCanceled } from '../utils.js';
|
|
7
7
|
import { createPageAssets, getLayouts } from './utils.js';
|
|
8
8
|
const __dirname = import.meta.dirname;
|
|
@@ -36,6 +36,7 @@ export default async function bulkCreate(rootDir) {
|
|
|
36
36
|
const created = [];
|
|
37
37
|
const existing = [];
|
|
38
38
|
const errors = [];
|
|
39
|
+
const urls = [];
|
|
39
40
|
const spin = spinner();
|
|
40
41
|
spin.start('Generating page assets...');
|
|
41
42
|
await sleep();
|
|
@@ -49,8 +50,9 @@ export default async function bulkCreate(rootDir) {
|
|
|
49
50
|
}
|
|
50
51
|
else {
|
|
51
52
|
try {
|
|
52
|
-
await createPageAssets({ rootDir, contentPath, layout });
|
|
53
|
+
const url = await createPageAssets({ rootDir, contentPath, layout });
|
|
53
54
|
created.push(contentPath);
|
|
55
|
+
urls.push(url);
|
|
54
56
|
spin.message(`Generated: ${contentPath}`);
|
|
55
57
|
}
|
|
56
58
|
catch (err) {
|
|
@@ -71,6 +73,17 @@ export default async function bulkCreate(rootDir) {
|
|
|
71
73
|
}
|
|
72
74
|
insertNewLine = true;
|
|
73
75
|
}
|
|
76
|
+
if (urls.length) {
|
|
77
|
+
if (insertNewLine) {
|
|
78
|
+
result.push('');
|
|
79
|
+
}
|
|
80
|
+
result.push('Page URLs:');
|
|
81
|
+
const sortedUrls = [...urls].sort();
|
|
82
|
+
for (const u of sortedUrls) {
|
|
83
|
+
result.push(` - ${u}`);
|
|
84
|
+
}
|
|
85
|
+
insertNewLine = true;
|
|
86
|
+
}
|
|
74
87
|
if (existing.length) {
|
|
75
88
|
if (insertNewLine) {
|
|
76
89
|
result.push('');
|
|
@@ -116,5 +129,5 @@ export default async function bulkCreate(rootDir) {
|
|
|
116
129
|
deleteSpinner.error('An error occurred while attempting to delete the file');
|
|
117
130
|
}
|
|
118
131
|
}
|
|
119
|
-
|
|
132
|
+
return urls;
|
|
120
133
|
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { intro, select, spinner, outro } from '@clack/prompts';
|
|
3
|
+
import { handleCanceled, sleep } from '../utils.js';
|
|
4
|
+
import { appendUrlsToPagesData, pagesDataPath } from './utils.js';
|
|
3
5
|
export default async function ({ root } = {}) {
|
|
4
6
|
const rootDir = root ?? process.cwd();
|
|
5
7
|
intro('Page Manager');
|
|
@@ -13,12 +15,40 @@ export default async function ({ root } = {}) {
|
|
|
13
15
|
value: 'bulk'
|
|
14
16
|
}]
|
|
15
17
|
}));
|
|
18
|
+
let urls;
|
|
16
19
|
if (action === 'bulk') {
|
|
17
20
|
const bulkCreate = await import('./bulk-create.js');
|
|
18
|
-
await bulkCreate.default(rootDir);
|
|
21
|
+
urls = await bulkCreate.default(rootDir);
|
|
19
22
|
}
|
|
20
23
|
else {
|
|
21
24
|
const singleCreate = await import('./single-create.js');
|
|
22
|
-
await singleCreate.default(rootDir);
|
|
25
|
+
const url = await singleCreate.default(rootDir);
|
|
26
|
+
urls = [url];
|
|
23
27
|
}
|
|
28
|
+
if (urls.length && existsSync(pagesDataPath(rootDir))) {
|
|
29
|
+
const update = handleCanceled(await select({
|
|
30
|
+
message: 'Would you like to append these URLs to the Pages array in src/data/pages.tsx?',
|
|
31
|
+
options: [{
|
|
32
|
+
label: 'Yes, update the file',
|
|
33
|
+
value: true
|
|
34
|
+
}, {
|
|
35
|
+
label: 'No, skip',
|
|
36
|
+
value: false
|
|
37
|
+
}]
|
|
38
|
+
}));
|
|
39
|
+
if (update) {
|
|
40
|
+
const spin = spinner();
|
|
41
|
+
spin.start('Updating src/data/pages.tsx');
|
|
42
|
+
await sleep();
|
|
43
|
+
try {
|
|
44
|
+
await appendUrlsToPagesData(rootDir, urls);
|
|
45
|
+
spin.stop('src/data/pages.tsx updated!');
|
|
46
|
+
}
|
|
47
|
+
catch (err) {
|
|
48
|
+
spin.stop(`Could not update src/data/pages.tsx: ${err.message}`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
outro('Happy coding!');
|
|
53
|
+
return urls;
|
|
24
54
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export default function singleCreate(rootDir: string): Promise<
|
|
1
|
+
export default function singleCreate(rootDir: string): Promise<string>;
|
|
@@ -50,9 +50,11 @@ export default async function singleCreate(rootDir) {
|
|
|
50
50
|
const spin = spinner();
|
|
51
51
|
spin.start('Generating page assets');
|
|
52
52
|
await sleep();
|
|
53
|
-
await createPageAssets({ rootDir, contentPath, contentName, pageName, layout });
|
|
53
|
+
const url = await createPageAssets({ rootDir, contentPath, contentName, pageName, layout });
|
|
54
54
|
await sleep();
|
|
55
55
|
spin.stop('Page assets generated!');
|
|
56
|
+
note(`Page URL: ${url}`);
|
|
57
|
+
return url;
|
|
56
58
|
// let path: string | undefined = undefined;
|
|
57
59
|
// do {
|
|
58
60
|
// path = handleCanceled(await text({
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
export declare function pagesDataPath(rootDir: string): string;
|
|
2
|
+
export declare function titleFromUrl(url: string): string;
|
|
3
|
+
export declare function appendUrlsToPagesData(rootDir: string, urls: string[]): Promise<void>;
|
|
1
4
|
export declare function getLayouts(rootDir: string): Promise<string[]>;
|
|
2
5
|
interface CreateParams {
|
|
3
6
|
rootDir: string;
|
|
@@ -6,5 +9,5 @@ interface CreateParams {
|
|
|
6
9
|
pageName?: string;
|
|
7
10
|
layout: string;
|
|
8
11
|
}
|
|
9
|
-
export declare function createPageAssets({ rootDir, contentPath, contentName, pageName, layout }: CreateParams): Promise<
|
|
12
|
+
export declare function createPageAssets({ rootDir, contentPath, contentName, pageName, layout }: CreateParams): Promise<string>;
|
|
10
13
|
export {};
|
|
@@ -1,6 +1,61 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import { readFile, writeFile, readdir, mkdir } from 'node:fs/promises';
|
|
3
3
|
import { basename, join } from 'node:path';
|
|
4
|
+
export function pagesDataPath(rootDir) {
|
|
5
|
+
return join(rootDir, 'src/data/pages.tsx');
|
|
6
|
+
}
|
|
7
|
+
export function titleFromUrl(url) {
|
|
8
|
+
return url
|
|
9
|
+
.replace(/^\/+/, '')
|
|
10
|
+
.replace(/\/+$/, '')
|
|
11
|
+
.split('-')
|
|
12
|
+
.filter(Boolean)
|
|
13
|
+
.map(w => w.charAt(0).toUpperCase() + w.slice(1))
|
|
14
|
+
.join(' ');
|
|
15
|
+
}
|
|
16
|
+
export async function appendUrlsToPagesData(rootDir, urls) {
|
|
17
|
+
if (!urls.length)
|
|
18
|
+
return;
|
|
19
|
+
const filePath = pagesDataPath(rootDir);
|
|
20
|
+
const content = await readFile(filePath, 'utf8');
|
|
21
|
+
const declMatch = content.match(/((?:export\s+)?const\s+Pages\b[^=]*=\s*\[)/);
|
|
22
|
+
if (!declMatch || declMatch.index === undefined) {
|
|
23
|
+
throw new Error('Could not locate the `Pages` array in src/data/pages.tsx');
|
|
24
|
+
}
|
|
25
|
+
const arrayStart = declMatch.index + declMatch[0].length - 1;
|
|
26
|
+
let depth = 0;
|
|
27
|
+
let arrayEnd = -1;
|
|
28
|
+
for (let i = arrayStart; i < content.length; i++) {
|
|
29
|
+
const ch = content[i];
|
|
30
|
+
if (ch === '[')
|
|
31
|
+
depth++;
|
|
32
|
+
else if (ch === ']') {
|
|
33
|
+
depth--;
|
|
34
|
+
if (depth === 0) {
|
|
35
|
+
arrayEnd = i;
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
if (arrayEnd === -1) {
|
|
41
|
+
throw new Error('Could not find the end of the `Pages` array in src/data/pages.tsx');
|
|
42
|
+
}
|
|
43
|
+
const before = content.slice(0, arrayEnd);
|
|
44
|
+
const after = content.slice(arrayEnd);
|
|
45
|
+
const indentMatch = before.slice(arrayStart + 1).match(/\n([ \t]+)\S/);
|
|
46
|
+
const indent = indentMatch ? indentMatch[1] : ' ';
|
|
47
|
+
const body = before.slice(arrayStart + 1);
|
|
48
|
+
const trimmedBody = body.replace(/\s+$/, '');
|
|
49
|
+
const needsComma = trimmedBody.length > 0 && !trimmedBody.endsWith(',');
|
|
50
|
+
const entries = urls
|
|
51
|
+
.map(url => `${indent}{ title: <>${titleFromUrl(url)}</>, href: '${url}' },`)
|
|
52
|
+
.join('\n');
|
|
53
|
+
const newBefore = content.slice(0, arrayStart + 1) +
|
|
54
|
+
(trimmedBody ? trimmedBody + (needsComma ? ',' : '') + '\n' : '\n') +
|
|
55
|
+
entries +
|
|
56
|
+
'\n';
|
|
57
|
+
await writeFile(filePath, newBefore + after);
|
|
58
|
+
}
|
|
4
59
|
export async function getLayouts(rootDir) {
|
|
5
60
|
const layouts = await readdir(join(rootDir, 'src/layouts'), { recursive: true });
|
|
6
61
|
return layouts.filter(l => l.endsWith('.astro')).sort();
|
|
@@ -54,4 +109,5 @@ export async function createPageAssets({ rootDir, contentPath, contentName, page
|
|
|
54
109
|
await writeFile(join(contentDir, contentPath, `${contentName}.astro`), contentTemplate);
|
|
55
110
|
await writeFile(join(contentDir, contentPath, `${contentName}.scss`), '');
|
|
56
111
|
await writeFile(join(contentDir, contentPath, `${contentName}.ts`), '');
|
|
112
|
+
return `/${pageName.replace(/\.astro$/, '')}/`;
|
|
57
113
|
}
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -4,13 +4,13 @@ import { join, resolve as resolvePath } from 'node:path';
|
|
|
4
4
|
|
|
5
5
|
import { parse } from 'yaml';
|
|
6
6
|
|
|
7
|
-
import { intro, text, select, multiselect, note, spinner,
|
|
7
|
+
import { intro, text, select, multiselect, note, spinner, box } from '@clack/prompts';
|
|
8
8
|
import { sleep, handleCanceled, cleanInput, generateRandomId, titleCase } from '../utils.js';
|
|
9
9
|
import { createPageAssets, getLayouts } from './utils.js';
|
|
10
10
|
|
|
11
11
|
const __dirname = import.meta.dirname;
|
|
12
12
|
|
|
13
|
-
export default async function bulkCreate(rootDir: string) {
|
|
13
|
+
export default async function bulkCreate(rootDir: string): Promise<string[]> {
|
|
14
14
|
const contentDir = join(rootDir, 'src/contents');
|
|
15
15
|
const pagesDir = join(rootDir, 'src/pages');
|
|
16
16
|
|
|
@@ -41,6 +41,7 @@ export default async function bulkCreate(rootDir: string) {
|
|
|
41
41
|
const created: string[] = [];
|
|
42
42
|
const existing: string[] = [];
|
|
43
43
|
const errors: string[] = [];
|
|
44
|
+
const urls: string[] = [];
|
|
44
45
|
|
|
45
46
|
const spin = spinner();
|
|
46
47
|
spin.start('Generating page assets...');
|
|
@@ -56,8 +57,9 @@ export default async function bulkCreate(rootDir: string) {
|
|
|
56
57
|
await sleep();
|
|
57
58
|
} else {
|
|
58
59
|
try {
|
|
59
|
-
await createPageAssets({ rootDir, contentPath, layout });
|
|
60
|
+
const url = await createPageAssets({ rootDir, contentPath, layout });
|
|
60
61
|
created.push(contentPath);
|
|
62
|
+
urls.push(url);
|
|
61
63
|
spin.message(`Generated: ${contentPath}`);
|
|
62
64
|
} catch (err) {
|
|
63
65
|
errors.push(contentPath);
|
|
@@ -79,6 +81,17 @@ export default async function bulkCreate(rootDir: string) {
|
|
|
79
81
|
}
|
|
80
82
|
insertNewLine = true;
|
|
81
83
|
}
|
|
84
|
+
if (urls.length) {
|
|
85
|
+
if (insertNewLine) {
|
|
86
|
+
result.push('');
|
|
87
|
+
}
|
|
88
|
+
result.push('Page URLs:');
|
|
89
|
+
const sortedUrls = [...urls].sort();
|
|
90
|
+
for (const u of sortedUrls) {
|
|
91
|
+
result.push(` - ${u}`);
|
|
92
|
+
}
|
|
93
|
+
insertNewLine = true;
|
|
94
|
+
}
|
|
82
95
|
if (existing.length) {
|
|
83
96
|
if (insertNewLine) {
|
|
84
97
|
result.push('');
|
|
@@ -126,6 +139,5 @@ export default async function bulkCreate(rootDir: string) {
|
|
|
126
139
|
}
|
|
127
140
|
}
|
|
128
141
|
|
|
129
|
-
|
|
130
|
-
|
|
142
|
+
return urls;
|
|
131
143
|
}
|
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { intro, select } from '@clack/prompts';
|
|
3
|
-
import { handleCanceled } from '../utils.js';
|
|
1
|
+
import { existsSync } from 'node:fs';
|
|
2
|
+
import { intro, select, spinner, outro } from '@clack/prompts';
|
|
3
|
+
import { handleCanceled, sleep } from '../utils.js';
|
|
4
|
+
import { appendUrlsToPagesData, pagesDataPath } from './utils.js';
|
|
4
5
|
|
|
5
6
|
interface Opts {
|
|
6
7
|
root?: string;
|
|
7
8
|
}
|
|
8
|
-
export default async function({ root }: Opts = {}) {
|
|
9
|
+
export default async function({ root }: Opts = {}): Promise<string[]> {
|
|
9
10
|
const rootDir = root ?? process.cwd();
|
|
10
11
|
|
|
11
12
|
intro('Page Manager');
|
|
@@ -21,11 +22,41 @@ export default async function({ root }: Opts = {}) {
|
|
|
21
22
|
}]
|
|
22
23
|
}));
|
|
23
24
|
|
|
25
|
+
let urls: string[];
|
|
24
26
|
if (action === 'bulk') {
|
|
25
27
|
const bulkCreate = await import('./bulk-create.js');
|
|
26
|
-
await bulkCreate.default(rootDir);
|
|
28
|
+
urls = await bulkCreate.default(rootDir);
|
|
27
29
|
} else {
|
|
28
30
|
const singleCreate = await import('./single-create.js');
|
|
29
|
-
await singleCreate.default(rootDir);
|
|
31
|
+
const url = await singleCreate.default(rootDir);
|
|
32
|
+
urls = [url];
|
|
30
33
|
}
|
|
34
|
+
|
|
35
|
+
if (urls.length && existsSync(pagesDataPath(rootDir))) {
|
|
36
|
+
const update = handleCanceled<boolean>(await select({
|
|
37
|
+
message: 'Would you like to append these URLs to the Pages array in src/data/pages.tsx?',
|
|
38
|
+
options: [{
|
|
39
|
+
label: 'Yes, update the file',
|
|
40
|
+
value: true
|
|
41
|
+
}, {
|
|
42
|
+
label: 'No, skip',
|
|
43
|
+
value: false
|
|
44
|
+
}]
|
|
45
|
+
}));
|
|
46
|
+
if (update) {
|
|
47
|
+
const spin = spinner();
|
|
48
|
+
spin.start('Updating src/data/pages.tsx');
|
|
49
|
+
await sleep();
|
|
50
|
+
try {
|
|
51
|
+
await appendUrlsToPagesData(rootDir, urls);
|
|
52
|
+
spin.stop('src/data/pages.tsx updated!');
|
|
53
|
+
} catch (err) {
|
|
54
|
+
spin.stop(`Could not update src/data/pages.tsx: ${(err as Error).message}`);
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
outro('Happy coding!');
|
|
60
|
+
|
|
61
|
+
return urls;
|
|
31
62
|
}
|
|
@@ -2,14 +2,14 @@ import { existsSync } from 'node:fs';
|
|
|
2
2
|
import { writeFile, readdir, readFile, mkdir } from 'node:fs/promises';
|
|
3
3
|
import { basename, join, resolve as resolvePath } from 'node:path';
|
|
4
4
|
|
|
5
|
-
import { text, select, multiselect, note, spinner
|
|
5
|
+
import { text, select, multiselect, note, spinner } from '@clack/prompts';
|
|
6
6
|
import { sleep, handleCanceled, cleanInput, generateRandomId, titleCase } from '../utils.js';
|
|
7
7
|
import { getLayouts, createPageAssets } from './utils.js';
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
export default async function singleCreate(rootDir: string) {
|
|
12
|
+
export default async function singleCreate(rootDir: string): Promise<string> {
|
|
13
13
|
const contentDir = join(rootDir, 'src/contents');
|
|
14
14
|
const pagesDir = join(rootDir, 'src/pages');
|
|
15
15
|
const layouts = await getLayouts(rootDir);
|
|
@@ -65,11 +65,15 @@ export default async function singleCreate(rootDir: string) {
|
|
|
65
65
|
spin.start('Generating page assets');
|
|
66
66
|
await sleep();
|
|
67
67
|
|
|
68
|
-
await createPageAssets({ rootDir, contentPath, contentName, pageName, layout });
|
|
68
|
+
const url = await createPageAssets({ rootDir, contentPath, contentName, pageName, layout });
|
|
69
69
|
|
|
70
70
|
await sleep();
|
|
71
71
|
spin.stop('Page assets generated!');
|
|
72
72
|
|
|
73
|
+
note(`Page URL: ${url}`);
|
|
74
|
+
|
|
75
|
+
return url;
|
|
76
|
+
|
|
73
77
|
|
|
74
78
|
// let path: string | undefined = undefined;
|
|
75
79
|
// do {
|
|
@@ -2,6 +2,72 @@ import { existsSync } from 'node:fs';
|
|
|
2
2
|
import { readFile, writeFile, readdir, mkdir } from 'node:fs/promises';
|
|
3
3
|
import { basename, join, resolve as resolvePath } from 'node:path';
|
|
4
4
|
|
|
5
|
+
export function pagesDataPath(rootDir: string): string {
|
|
6
|
+
return join(rootDir, 'src/data/pages.tsx');
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function titleFromUrl(url: string): string {
|
|
10
|
+
return url
|
|
11
|
+
.replace(/^\/+/, '')
|
|
12
|
+
.replace(/\/+$/, '')
|
|
13
|
+
.split('-')
|
|
14
|
+
.filter(Boolean)
|
|
15
|
+
.map(w => w.charAt(0).toUpperCase() + w.slice(1))
|
|
16
|
+
.join(' ');
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export async function appendUrlsToPagesData(rootDir: string, urls: string[]): Promise<void> {
|
|
20
|
+
if (!urls.length) return;
|
|
21
|
+
|
|
22
|
+
const filePath = pagesDataPath(rootDir);
|
|
23
|
+
const content = await readFile(filePath, 'utf8');
|
|
24
|
+
|
|
25
|
+
const declMatch = content.match(/((?:export\s+)?const\s+Pages\b[^=]*=\s*\[)/);
|
|
26
|
+
if (!declMatch || declMatch.index === undefined) {
|
|
27
|
+
throw new Error('Could not locate the `Pages` array in src/data/pages.tsx');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const arrayStart = declMatch.index + declMatch[0].length - 1;
|
|
31
|
+
let depth = 0;
|
|
32
|
+
let arrayEnd = -1;
|
|
33
|
+
for (let i = arrayStart; i < content.length; i++) {
|
|
34
|
+
const ch = content[i];
|
|
35
|
+
if (ch === '[') depth++;
|
|
36
|
+
else if (ch === ']') {
|
|
37
|
+
depth--;
|
|
38
|
+
if (depth === 0) {
|
|
39
|
+
arrayEnd = i;
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
if (arrayEnd === -1) {
|
|
45
|
+
throw new Error('Could not find the end of the `Pages` array in src/data/pages.tsx');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const before = content.slice(0, arrayEnd);
|
|
49
|
+
const after = content.slice(arrayEnd);
|
|
50
|
+
|
|
51
|
+
const indentMatch = before.slice(arrayStart + 1).match(/\n([ \t]+)\S/);
|
|
52
|
+
const indent = indentMatch ? indentMatch[1] : ' ';
|
|
53
|
+
|
|
54
|
+
const body = before.slice(arrayStart + 1);
|
|
55
|
+
const trimmedBody = body.replace(/\s+$/, '');
|
|
56
|
+
const needsComma = trimmedBody.length > 0 && !trimmedBody.endsWith(',');
|
|
57
|
+
|
|
58
|
+
const entries = urls
|
|
59
|
+
.map(url => `${indent}{ title: <>${titleFromUrl(url)}</>, href: '${url}' },`)
|
|
60
|
+
.join('\n');
|
|
61
|
+
|
|
62
|
+
const newBefore =
|
|
63
|
+
content.slice(0, arrayStart + 1) +
|
|
64
|
+
(trimmedBody ? trimmedBody + (needsComma ? ',' : '') + '\n' : '\n') +
|
|
65
|
+
entries +
|
|
66
|
+
'\n';
|
|
67
|
+
|
|
68
|
+
await writeFile(filePath, newBefore + after);
|
|
69
|
+
}
|
|
70
|
+
|
|
5
71
|
export async function getLayouts(rootDir: string): Promise<string[]> {
|
|
6
72
|
const layouts = await readdir(join(rootDir, 'src/layouts'), { recursive: true });
|
|
7
73
|
return layouts.filter(l => l.endsWith('.astro')).sort();
|
|
@@ -14,7 +80,7 @@ interface CreateParams {
|
|
|
14
80
|
pageName?: string;
|
|
15
81
|
layout: string;
|
|
16
82
|
}
|
|
17
|
-
export async function createPageAssets({ rootDir, contentPath, contentName, pageName, layout }: CreateParams) {
|
|
83
|
+
export async function createPageAssets({ rootDir, contentPath, contentName, pageName, layout }: CreateParams): Promise<string> {
|
|
18
84
|
const contentDir = join(rootDir, 'src/contents');
|
|
19
85
|
const pagesDir = join(rootDir, 'src/pages');
|
|
20
86
|
|
|
@@ -67,4 +133,6 @@ export async function createPageAssets({ rootDir, contentPath, contentName, page
|
|
|
67
133
|
await writeFile(join(contentDir, contentPath, `${contentName}.astro`), contentTemplate);
|
|
68
134
|
await writeFile(join(contentDir, contentPath, `${contentName}.scss`), '');
|
|
69
135
|
await writeFile(join(contentDir, contentPath, `${contentName}.ts`), '');
|
|
136
|
+
|
|
137
|
+
return `/${pageName.replace(/\.astro$/, '')}/`;
|
|
70
138
|
}
|
|
@@ -5,13 +5,15 @@ import esbuild from 'esbuild';
|
|
|
5
5
|
import script from './resize.ts?raw';
|
|
6
6
|
|
|
7
7
|
export interface Props {
|
|
8
|
-
width: number
|
|
9
|
-
height: number
|
|
8
|
+
width: number;
|
|
9
|
+
height: number;
|
|
10
|
+
scale?: 'down' | 'up' | 'always' | 'never';
|
|
10
11
|
}
|
|
11
12
|
|
|
12
13
|
const {
|
|
13
14
|
width,
|
|
14
|
-
height
|
|
15
|
+
height,
|
|
16
|
+
scale = 'down'
|
|
15
17
|
} = Astro.props;
|
|
16
18
|
|
|
17
19
|
let js: string | undefined;
|
|
@@ -22,7 +24,8 @@ if (BUILD_TARGET === 'veeva' || WEB_SCALING) {
|
|
|
22
24
|
minify: true,
|
|
23
25
|
define: {
|
|
24
26
|
IVA_WIDTH: width.toString(),
|
|
25
|
-
IVA_HEIGHT: height.toString()
|
|
27
|
+
IVA_HEIGHT: height.toString(),
|
|
28
|
+
IVA_SCALE: JSON.stringify(scale)
|
|
26
29
|
}
|
|
27
30
|
});
|
|
28
31
|
js = code;
|
|
@@ -7,6 +7,8 @@
|
|
|
7
7
|
const width: number = IVA_WIDTH;
|
|
8
8
|
// @ts-ignore
|
|
9
9
|
const height: number = IVA_HEIGHT;
|
|
10
|
+
// @ts-ignore
|
|
11
|
+
const scaleMode: 'down' | 'up' | 'always' | 'never' = IVA_SCALE;
|
|
10
12
|
// ----------------------------------------------------------------------------------------------------
|
|
11
13
|
|
|
12
14
|
const scalingWrapper = document.querySelector('.scaling-wrapper') as HTMLElement;
|
|
@@ -24,19 +26,30 @@
|
|
|
24
26
|
const vw = Math.round(visualViewport?.width ?? window.innerWidth);
|
|
25
27
|
const vh = Math.round(visualViewport?.height ?? window.innerHeight);
|
|
26
28
|
let transform: string | undefined;
|
|
27
|
-
|
|
28
|
-
if (
|
|
29
|
-
const scale = Math.min(vw / width, vh / height);
|
|
30
|
-
if (scale !== 1 && lastScale !== scale) {
|
|
31
|
-
lastScale = scale;
|
|
32
|
-
const offsetX = (vw - width * scale) / 2 / scale;
|
|
33
|
-
const offsetY = (vh - height * scale) / 2 / scale;
|
|
34
|
-
transform = `scale(${scale}) translate(${offsetX}px, ${offsetY}px)`;
|
|
35
|
-
}
|
|
36
|
-
} else {
|
|
29
|
+
|
|
30
|
+
if (scaleMode === 'never') {
|
|
37
31
|
transform = 'none';
|
|
32
|
+
} else {
|
|
33
|
+
const smallerThanTarget = vw < width - 1 || vh < height - 1;
|
|
34
|
+
const largerThanTarget = vw > width + 1 || vh > height + 1;
|
|
35
|
+
const shouldScale =
|
|
36
|
+
scaleMode === 'always' ||
|
|
37
|
+
(scaleMode === 'down' && smallerThanTarget) ||
|
|
38
|
+
(scaleMode === 'up' && largerThanTarget);
|
|
39
|
+
|
|
40
|
+
if (shouldScale) {
|
|
41
|
+
const scale = Math.min(vw / width, vh / height);
|
|
42
|
+
if (scale !== 1 && lastScale !== scale) {
|
|
43
|
+
lastScale = scale;
|
|
44
|
+
const offsetX = (vw - width * scale) / 2 / scale;
|
|
45
|
+
const offsetY = (vh - height * scale) / 2 / scale;
|
|
46
|
+
transform = `scale(${scale}) translate(${offsetX}px, ${offsetY}px)`;
|
|
47
|
+
}
|
|
48
|
+
} else {
|
|
49
|
+
transform = 'none';
|
|
50
|
+
}
|
|
38
51
|
}
|
|
39
|
-
|
|
52
|
+
|
|
40
53
|
if (transform) {
|
|
41
54
|
document.body.classList[transform === 'none' ? 'remove' : 'add']('scaled');
|
|
42
55
|
scalingWrapper.style.transform = transform;
|