mixdog 0.9.154 → 0.9.155
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/package.json +2 -2
- package/scripts/build-voice-runtime-unix.sh +24 -0
- package/scripts/voice-runtime-config.json +1 -1
- package/src/runtime/agent/orchestrator/providers/anthropic-messages.mjs +2 -2
- package/src/runtime/agent/orchestrator/providers/anthropic-oauth.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/anthropic.mjs +5 -2
- package/src/runtime/agent/orchestrator/providers/lib/anthropic-request-utils.mjs +47 -4
- package/src/runtime/agent/orchestrator/session/manager/ask-session.mjs +12 -0
- package/src/runtime/agent/orchestrator/session/pre-send-compact.mjs +7 -0
- package/src/runtime/agent/orchestrator/session/store/live-state.mjs +4 -1
- package/src/runtime/channels/data/voice-runtime-manifest.json +17 -17
- package/src/runtime/channels/lib/voice-runtime-fetcher.mjs +59 -0
- package/src/runtime/office/assurance-benchmark.mjs +67 -1
- package/src/runtime/office/assurance.mjs +24 -17
- package/src/runtime/office/capabilities.mjs +17 -17
- package/src/runtime/office/composition-system.mjs +451 -0
- package/src/runtime/office/design-library.mjs +102 -0
- package/src/runtime/office/design-system.mjs +357 -168
- package/src/runtime/office/index.mjs +149 -20
- package/src/runtime/office/office-com-host.ps1 +132 -46
- package/src/runtime/office/pdf-render.mjs +203 -35
- package/src/runtime/office/portable-ooxml.mjs +1090 -47
- package/src/runtime/office/portable-package.mjs +390 -0
- package/src/runtime/office/portable-sheet-styles.mjs +241 -0
- package/src/runtime/office/portable-slide-shapes.mjs +283 -0
- package/src/runtime/office/quality-live-benchmark.mjs +82 -36
- package/src/runtime/office/quality-pipeline.mjs +3 -0
- package/src/runtime/office/tool-defs.mjs +2 -2
- package/src/session-runtime/goal-reminder.mjs +79 -0
- package/src/session-runtime/goal-runtime.mjs +105 -45
- package/src/session-runtime/goal-text.mjs +58 -0
- package/src/session-runtime/runtime-core.mjs +14 -1
- package/src/session-runtime/session-lifecycle.mjs +1 -1
- package/src/session-runtime/session-turn-api.mjs +5 -0
- package/src/standalone/session-service.mjs +30 -3
- package/src/tui/dist/index.mjs +12 -3
- package/src/tui/lib/voice-setup.mjs +19 -3
- package/src/tui/session/goal-continuation.mjs +7 -0
|
@@ -35,6 +35,7 @@ import {
|
|
|
35
35
|
validateLibreOfficeReopen,
|
|
36
36
|
validatePortableOoxml,
|
|
37
37
|
} from './portable-ooxml.mjs';
|
|
38
|
+
import { createPortableOoxmlDocument, portableCreateSupported } from './portable-package.mjs';
|
|
38
39
|
import {
|
|
39
40
|
applyPdfBatch,
|
|
40
41
|
createPdf,
|
|
@@ -83,10 +84,12 @@ import {
|
|
|
83
84
|
reviewOfficeDesign,
|
|
84
85
|
reviewPptxVisualCritique,
|
|
85
86
|
} from './design-system.mjs';
|
|
87
|
+
import { summarizeOfficeCompositions } from './composition-system.mjs';
|
|
86
88
|
import {
|
|
87
89
|
createPptxSlideSelection,
|
|
88
90
|
inspectOfficeDesignLibrary,
|
|
89
91
|
persistOfficeDesignBinding,
|
|
92
|
+
recordOfficeCompositionHistory,
|
|
90
93
|
resolveOfficeDesignLibrary,
|
|
91
94
|
} from './design-library.mjs';
|
|
92
95
|
import {
|
|
@@ -901,6 +904,28 @@ async function selectMode(requested, format, source) {
|
|
|
901
904
|
return { mode: 'portable', backend: 'mixdog-ooxml' };
|
|
902
905
|
}
|
|
903
906
|
|
|
907
|
+
async function seedPortableDeck(source, slides, target) {
|
|
908
|
+
const selected = Array.isArray(slides) ? slides.map(Number).filter(Number.isInteger) : [];
|
|
909
|
+
if (selected.length) {
|
|
910
|
+
const selection = await createPptxSlideSelection(source, selected, target);
|
|
911
|
+
return { count: selection.count };
|
|
912
|
+
}
|
|
913
|
+
await copyFile(source, target);
|
|
914
|
+
const zip = await JSZip.loadAsync(await readFile(target));
|
|
915
|
+
return {
|
|
916
|
+
count: Object.keys(zip.files).filter((name) => /^ppt\/slides\/slide\d+\.xml$/.test(name)).length,
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
|
|
920
|
+
async function selectCreateMode(requested, format, target) {
|
|
921
|
+
if (requested === 'portable') return { mode: 'portable', backend: 'mixdog-ooxml' };
|
|
922
|
+
if (requested !== 'auto') return await selectMode(requested, format, target);
|
|
923
|
+
const detected = await selectMode('auto', format, target);
|
|
924
|
+
return detected.backend === 'microsoft-office-com'
|
|
925
|
+
? { mode: 'visible', backend: 'microsoft-office-com' }
|
|
926
|
+
: detected;
|
|
927
|
+
}
|
|
928
|
+
|
|
904
929
|
async function openSession(args, cwd, dataDir) {
|
|
905
930
|
const source = fullPath(args.path, cwd);
|
|
906
931
|
if (!await exists(source)) throw new Error(`Office document not found: ${source}`);
|
|
@@ -947,6 +972,7 @@ async function openSession(args, cwd, dataDir) {
|
|
|
947
972
|
semanticCount: 0,
|
|
948
973
|
requiresVisualReview: format === 'pptx' && designContext.design.review.required,
|
|
949
974
|
slidePlans: [],
|
|
975
|
+
compositions: [],
|
|
950
976
|
},
|
|
951
977
|
};
|
|
952
978
|
if (selected.backend === 'microsoft-office-com') {
|
|
@@ -1021,7 +1047,7 @@ async function createSession(args, cwd, dataDir) {
|
|
|
1021
1047
|
designRequest,
|
|
1022
1048
|
designLibrary,
|
|
1023
1049
|
design,
|
|
1024
|
-
designState: { renderedVersion: null, semanticCount: 0, requiresVisualReview: false },
|
|
1050
|
+
designState: { renderedVersion: null, semanticCount: 0, requiresVisualReview: false, compositions: [] },
|
|
1025
1051
|
};
|
|
1026
1052
|
await registerOfficeSession(session);
|
|
1027
1053
|
return session;
|
|
@@ -1056,18 +1082,52 @@ async function createSession(args, cwd, dataDir) {
|
|
|
1056
1082
|
designRequest,
|
|
1057
1083
|
designLibrary,
|
|
1058
1084
|
design,
|
|
1059
|
-
designState: { renderedVersion: null, semanticCount: 0, requiresVisualReview: false },
|
|
1085
|
+
designState: { renderedVersion: null, semanticCount: 0, requiresVisualReview: false, compositions: [] },
|
|
1060
1086
|
};
|
|
1061
1087
|
await registerOfficeSession(session);
|
|
1062
1088
|
return session;
|
|
1063
1089
|
}
|
|
1064
|
-
const requestedMode = String(args.mode || '
|
|
1065
|
-
if (['attach', 'live'
|
|
1066
|
-
throw new Error('Office create requires visible or
|
|
1090
|
+
const requestedMode = String(args.mode || 'auto').toLowerCase();
|
|
1091
|
+
if (['attach', 'live'].includes(requestedMode)) {
|
|
1092
|
+
throw new Error('Office create requires visible, background, or portable mode');
|
|
1067
1093
|
}
|
|
1068
|
-
const selected = await
|
|
1069
|
-
if (selected.backend
|
|
1070
|
-
|
|
1094
|
+
const selected = await selectCreateMode(requestedMode, format, target);
|
|
1095
|
+
if (selected.backend === 'mixdog-ooxml') {
|
|
1096
|
+
if (!portableCreateSupported(fileKind)) {
|
|
1097
|
+
throw new Error(`Creating .${fileKind} without Microsoft Office is unsupported; open Microsoft Office or choose a docx, xlsx, or pptx target`);
|
|
1098
|
+
}
|
|
1099
|
+
await mkdir(dirname(target), { recursive: true });
|
|
1100
|
+
await createPortableOoxmlDocument(target, {
|
|
1101
|
+
fileKind,
|
|
1102
|
+
title: basename(target, extname(target)),
|
|
1103
|
+
});
|
|
1104
|
+
const session = {
|
|
1105
|
+
id: `office_${randomUUID().replaceAll('-', '').slice(0, 16)}`,
|
|
1106
|
+
source: target,
|
|
1107
|
+
target,
|
|
1108
|
+
fileKind,
|
|
1109
|
+
format,
|
|
1110
|
+
mode: 'portable',
|
|
1111
|
+
backend: 'mixdog-ooxml',
|
|
1112
|
+
openedAt: new Date().toISOString(),
|
|
1113
|
+
dataDir,
|
|
1114
|
+
created: true,
|
|
1115
|
+
ownership: 'owned',
|
|
1116
|
+
visible: false,
|
|
1117
|
+
snapshotVersion: 0,
|
|
1118
|
+
designRequest,
|
|
1119
|
+
designLibrary,
|
|
1120
|
+
design,
|
|
1121
|
+
designState: {
|
|
1122
|
+
renderedVersion: null,
|
|
1123
|
+
semanticCount: 0,
|
|
1124
|
+
requiresVisualReview: format === 'pptx' && design.review.required,
|
|
1125
|
+
slidePlans: [],
|
|
1126
|
+
compositions: [],
|
|
1127
|
+
},
|
|
1128
|
+
};
|
|
1129
|
+
await registerOfficeSession(session);
|
|
1130
|
+
return session;
|
|
1071
1131
|
}
|
|
1072
1132
|
const id = `office_${randomUUID().replaceAll('-', '').slice(0, 16)}`;
|
|
1073
1133
|
const opened = await openMicrosoftOfficeSession({
|
|
@@ -1106,6 +1166,7 @@ async function createSession(args, cwd, dataDir) {
|
|
|
1106
1166
|
semanticCount: 0,
|
|
1107
1167
|
requiresVisualReview: format === 'pptx' && design.review.required,
|
|
1108
1168
|
slidePlans: [],
|
|
1169
|
+
compositions: [],
|
|
1109
1170
|
},
|
|
1110
1171
|
};
|
|
1111
1172
|
await registerOfficeSession(session);
|
|
@@ -1270,16 +1331,13 @@ async function applyBatch(session, args) {
|
|
|
1270
1331
|
});
|
|
1271
1332
|
session.designRequest = designRequest;
|
|
1272
1333
|
session.design = prepared.design;
|
|
1273
|
-
session.designState ||= {
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
}
|
|
1281
|
-
session.designState.slidePlans = [...existingPlans.values()].sort((left, right) => left.slide - right.slide);
|
|
1282
|
-
}
|
|
1334
|
+
session.designState ||= {
|
|
1335
|
+
renderedVersion: null,
|
|
1336
|
+
semanticCount: 0,
|
|
1337
|
+
requiresVisualReview: false,
|
|
1338
|
+
slidePlans: [],
|
|
1339
|
+
compositions: [],
|
|
1340
|
+
};
|
|
1283
1341
|
const pathOperations = new Set(['add_image', 'replace_image', 'stamp_image', 'add_attachment', 'merge_pdf', 'apply_theme', 'import_slides', 'add_media']);
|
|
1284
1342
|
const operations = Array.isArray(prepared.operations)
|
|
1285
1343
|
? prepared.operations.map((operation) => {
|
|
@@ -1327,6 +1385,13 @@ async function applyBatch(session, args) {
|
|
|
1327
1385
|
&& operations[0].op === 'import_slides'
|
|
1328
1386
|
&& Number(operations[0].after || 0) === 0
|
|
1329
1387
|
&& extname(operations[0].path).toLowerCase() === extname(target).toLowerCase();
|
|
1388
|
+
const portableDeckSeed = session.backend === 'mixdog-ooxml'
|
|
1389
|
+
&& session.format === 'pptx'
|
|
1390
|
+
&& session.created === true
|
|
1391
|
+
&& Number(session.snapshotVersion || 0) === 0
|
|
1392
|
+
&& operations[0].op === 'import_slides'
|
|
1393
|
+
&& Number(operations[0].after || 0) === 0
|
|
1394
|
+
&& extname(operations[0].path).toLowerCase() === extname(target).toLowerCase();
|
|
1330
1395
|
const needsComCheckpoint = isMicrosoftOfficeSession(session)
|
|
1331
1396
|
&& session.mode === 'background'
|
|
1332
1397
|
&& operations.some((operation) => operation.op === 'import_slides');
|
|
@@ -1429,6 +1494,20 @@ async function applyBatch(session, args) {
|
|
|
1429
1494
|
});
|
|
1430
1495
|
} else if (TABULAR_FORMATS.has(session.format)) {
|
|
1431
1496
|
results = await applyTabularBatch(target, session.format, operations);
|
|
1497
|
+
} else if (portableDeckSeed) {
|
|
1498
|
+
const operation = operations[0];
|
|
1499
|
+
const seeded = await seedPortableDeck(operation.path, operation.slides, target);
|
|
1500
|
+
const remaining = operations.slice(1);
|
|
1501
|
+
results = [
|
|
1502
|
+
{
|
|
1503
|
+
op: 'import_slides',
|
|
1504
|
+
changed: true,
|
|
1505
|
+
count: seeded.count,
|
|
1506
|
+
source: operation.path,
|
|
1507
|
+
seeded: true,
|
|
1508
|
+
},
|
|
1509
|
+
...(remaining.length ? await applyPortableOoxmlBatch(target, session.format, remaining) : []),
|
|
1510
|
+
];
|
|
1432
1511
|
} else {
|
|
1433
1512
|
results = await applyPortableOoxmlBatch(target, session.format, operations);
|
|
1434
1513
|
}
|
|
@@ -1456,6 +1535,19 @@ async function applyBatch(session, args) {
|
|
|
1456
1535
|
transactionResult = transactionView(transaction);
|
|
1457
1536
|
if (journalWarning) transactionResult.journalWarning = journalWarning;
|
|
1458
1537
|
}
|
|
1538
|
+
session.designState.semanticCount += prepared.semantic.length;
|
|
1539
|
+
session.designState.compositions = [
|
|
1540
|
+
...(session.designState.compositions || []),
|
|
1541
|
+
...prepared.semantic.map((entry) => entry?.composition).filter(Boolean),
|
|
1542
|
+
];
|
|
1543
|
+
if (session.format === 'pptx' && session.design.review.required) {
|
|
1544
|
+
session.designState.requiresVisualReview = true;
|
|
1545
|
+
const existingPlans = new Map((session.designState.slidePlans || []).map((plan) => [Number(plan.slide), plan]));
|
|
1546
|
+
for (const semantic of prepared.semantic) {
|
|
1547
|
+
if (semantic?.plan && Number(semantic.slide) > 0) existingPlans.set(Number(semantic.slide), semantic.plan);
|
|
1548
|
+
}
|
|
1549
|
+
session.designState.slidePlans = [...existingPlans.values()].sort((left, right) => left.slide - right.slide);
|
|
1550
|
+
}
|
|
1459
1551
|
session.snapshotVersion = Number(session.snapshotVersion || 0) + 1;
|
|
1460
1552
|
session.designState.renderedVersion = null;
|
|
1461
1553
|
return {
|
|
@@ -1757,6 +1849,7 @@ async function qa(session, args, cwd) {
|
|
|
1757
1849
|
...(session.format === 'pptx' ? {
|
|
1758
1850
|
slidePlans: session.designState?.slidePlans || [],
|
|
1759
1851
|
} : {}),
|
|
1852
|
+
compositions: session.designState?.compositions || [],
|
|
1760
1853
|
},
|
|
1761
1854
|
library: session.designLibrary,
|
|
1762
1855
|
auditProfile: args.auditProfile,
|
|
@@ -2078,7 +2171,28 @@ async function finalize(session, args, cwd, signal) {
|
|
|
2078
2171
|
_images: reviewImages,
|
|
2079
2172
|
};
|
|
2080
2173
|
}
|
|
2174
|
+
const composition = summarizeOfficeCompositions(
|
|
2175
|
+
session.format,
|
|
2176
|
+
session.designState?.compositions || [],
|
|
2177
|
+
);
|
|
2081
2178
|
const closed = await timedStep('close', async () => await closeSession(session, { save: false, signal }));
|
|
2179
|
+
let compositionHistory = null;
|
|
2180
|
+
let compositionHistoryWarning = '';
|
|
2181
|
+
if (session.created && composition.fingerprint) {
|
|
2182
|
+
try {
|
|
2183
|
+
compositionHistory = await recordOfficeCompositionHistory(session.dataDir, {
|
|
2184
|
+
documentPath: session.target,
|
|
2185
|
+
format: session.format,
|
|
2186
|
+
profile: session.design?.profile,
|
|
2187
|
+
purpose: session.design?.purpose,
|
|
2188
|
+
expressionMode: session.design?.expressionMode,
|
|
2189
|
+
fingerprint: composition.fingerprint,
|
|
2190
|
+
compositionIds: composition.compositionIds,
|
|
2191
|
+
});
|
|
2192
|
+
} catch (error) {
|
|
2193
|
+
compositionHistoryWarning = error?.message || String(error);
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2082
2196
|
return {
|
|
2083
2197
|
ok: true,
|
|
2084
2198
|
finalized: true,
|
|
@@ -2092,6 +2206,9 @@ async function finalize(session, args, cwd, signal) {
|
|
|
2092
2206
|
review,
|
|
2093
2207
|
validation,
|
|
2094
2208
|
design: session.design,
|
|
2209
|
+
composition,
|
|
2210
|
+
compositionHistory,
|
|
2211
|
+
...(compositionHistoryWarning ? { compositionHistoryWarning } : {}),
|
|
2095
2212
|
stepMetrics,
|
|
2096
2213
|
_images: reviewImages,
|
|
2097
2214
|
};
|
|
@@ -2178,7 +2295,13 @@ export async function executeOfficeTool(args = {}, {
|
|
|
2178
2295
|
created: session.created === true,
|
|
2179
2296
|
});
|
|
2180
2297
|
Object.assign(session, designContext);
|
|
2181
|
-
session.designState = {
|
|
2298
|
+
session.designState = {
|
|
2299
|
+
renderedVersion: null,
|
|
2300
|
+
semanticCount: 0,
|
|
2301
|
+
requiresVisualReview: false,
|
|
2302
|
+
slidePlans: [],
|
|
2303
|
+
compositions: [],
|
|
2304
|
+
};
|
|
2182
2305
|
} else if (args.design) {
|
|
2183
2306
|
session.designRequest = mergeOfficeDesignRequest(session.designRequest, args.design);
|
|
2184
2307
|
session.design = resolveOfficeDesign(session.format, session.designRequest, { library: session.designLibrary });
|
|
@@ -2265,7 +2388,13 @@ export async function executeOfficeTool(args = {}, {
|
|
|
2265
2388
|
created: session.created === true,
|
|
2266
2389
|
});
|
|
2267
2390
|
Object.assign(session, designContext);
|
|
2268
|
-
session.designState = {
|
|
2391
|
+
session.designState = {
|
|
2392
|
+
renderedVersion: null,
|
|
2393
|
+
semanticCount: 0,
|
|
2394
|
+
requiresVisualReview: false,
|
|
2395
|
+
slidePlans: [],
|
|
2396
|
+
compositions: [],
|
|
2397
|
+
};
|
|
2269
2398
|
} else if (args.design) {
|
|
2270
2399
|
if (args.design.upgradeLibrary === true) {
|
|
2271
2400
|
const upgraded = await resolveOfficeDesignContext({
|
|
@@ -101,6 +101,7 @@ function Open-BackgroundDocument($app, [string]$format, [string]$path) {
|
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
function Snapshot-Word($doc, $payload) {
|
|
104
|
+
try { $null = $doc.Repaginate() } catch {}
|
|
104
105
|
$paragraphs = @()
|
|
105
106
|
$paragraphOffset = if ($payload.paged) { [Math]::Max(0, [int]$payload.offset) } else { 0 }
|
|
106
107
|
$paragraphLimit = if ($payload.paged) { [Math]::Max(1, [int]$payload.limit) } else { [int]$doc.Paragraphs.Count }
|
|
@@ -135,10 +136,12 @@ function Snapshot-Word($doc, $payload) {
|
|
|
135
136
|
style = $styleName
|
|
136
137
|
start = [int]$p.Range.Start
|
|
137
138
|
end = [int]$p.Range.End
|
|
139
|
+
inTable = $(try { [bool]$p.Range.Information(12) } catch { $false })
|
|
138
140
|
pageStart = $(try { [int]$p.Range.Information(3) } catch { 0 })
|
|
139
141
|
pageEnd = $(try {
|
|
140
142
|
$endRange = $p.Range.Duplicate
|
|
141
|
-
$endRange.
|
|
143
|
+
$end = [Math]::Max([int]$endRange.Start, [int]$endRange.End - 1)
|
|
144
|
+
$endRange.SetRange($end, $end)
|
|
142
145
|
[int]$endRange.Information(3)
|
|
143
146
|
} catch { 0 })
|
|
144
147
|
format = [ordered]@{
|
|
@@ -201,19 +204,20 @@ function Snapshot-Word($doc, $payload) {
|
|
|
201
204
|
} catch { 0 })
|
|
202
205
|
pageEnd = $(try {
|
|
203
206
|
$endRange = $table.Range.Duplicate
|
|
204
|
-
$endRange.
|
|
207
|
+
$end = [Math]::Max([int]$endRange.Start, [int]$endRange.End - 1)
|
|
208
|
+
$endRange.SetRange($end, $end)
|
|
205
209
|
[int]$endRange.Information(3)
|
|
206
210
|
} catch { 0 })
|
|
207
211
|
}
|
|
208
212
|
}
|
|
209
213
|
$blockOrder = @(
|
|
210
|
-
@($paragraphs | ForEach-Object {
|
|
214
|
+
@($paragraphs | Where-Object { -not [bool]$_.inTable } | ForEach-Object {
|
|
211
215
|
[ordered]@{ type = 'paragraph'; index = [int]$_.index; path = [string]$_.path; start = [int]$_.start }
|
|
212
216
|
})
|
|
213
217
|
@($tables | ForEach-Object {
|
|
214
218
|
[ordered]@{ type = 'table'; index = [int]$_.index; path = [string]$_.path; start = [int]$_.start }
|
|
215
219
|
})
|
|
216
|
-
) | Sort-Object start
|
|
220
|
+
) | Sort-Object { [int]$_['start'] }
|
|
217
221
|
$sections = @()
|
|
218
222
|
for ($sectionIndex = 1; $sectionIndex -le $doc.Sections.Count; $sectionIndex++) {
|
|
219
223
|
$section = $doc.Sections.Item($sectionIndex)
|
|
@@ -227,6 +231,7 @@ function Snapshot-Word($doc, $payload) {
|
|
|
227
231
|
try {
|
|
228
232
|
$collection = if ($location -eq 'header') { $section.Headers } else { $section.Footers }
|
|
229
233
|
$item = $collection.Item([int]$kind.value)
|
|
234
|
+
if (-not [bool]$item.Exists) { continue }
|
|
230
235
|
$stories += [ordered]@{
|
|
231
236
|
path = "/section[$sectionIndex]/${location}[$($kind.name)]"
|
|
232
237
|
kind = [string]$kind.name
|
|
@@ -1452,9 +1457,24 @@ function Apply-WordOperation($doc, $op) {
|
|
|
1452
1457
|
$paragraph = $doc.Paragraphs.Add($range)
|
|
1453
1458
|
$paragraph.Range.Text = "$text`r"
|
|
1454
1459
|
}
|
|
1460
|
+
$paragraphIndex = if ($reusedInitialParagraph) { 1 } else { [Math]::Max(1, [int]$doc.Paragraphs.Count - 1) }
|
|
1461
|
+
$paragraph = $doc.Paragraphs.Item($paragraphIndex)
|
|
1462
|
+
$props = $op.properties
|
|
1463
|
+
if ($props.listKind) {
|
|
1464
|
+
$kind = ([string]$props.listKind).ToLowerInvariant()
|
|
1465
|
+
if ($kind -eq 'number') {
|
|
1466
|
+
$paragraph.Range.ListFormat.ApplyNumberDefault()
|
|
1467
|
+
} elseif ($kind -ne 'none') {
|
|
1468
|
+
$paragraph.Range.ListFormat.ApplyBulletDefault()
|
|
1469
|
+
} else {
|
|
1470
|
+
$paragraph.Range.ListFormat.RemoveNumbers()
|
|
1471
|
+
}
|
|
1472
|
+
for ($level = 0; $level -lt [int]$props.listLevel; $level++) { $paragraph.Range.ListFormat.ListIndent() }
|
|
1473
|
+
} else {
|
|
1474
|
+
$paragraph.Range.ListFormat.RemoveNumbers()
|
|
1475
|
+
}
|
|
1455
1476
|
$style = if ($op.style) { [string]$op.style } elseif ($op.properties.style) { [string]$op.properties.style } else { '' }
|
|
1456
1477
|
if ($style) { $paragraph.Range.Style = Word-StyleValue $style }
|
|
1457
|
-
$props = $op.properties
|
|
1458
1478
|
if ($props.name) { $paragraph.Range.Font.Name = [string]$props.name }
|
|
1459
1479
|
if ($props.size) { $paragraph.Range.Font.Size = [single]$props.size }
|
|
1460
1480
|
if ($null -ne $props.bold) { $paragraph.Range.Font.Bold = if ($props.bold) { -1 } else { 0 } }
|
|
@@ -1483,20 +1503,6 @@ function Apply-WordOperation($doc, $op) {
|
|
|
1483
1503
|
$border.LineStyle = 1
|
|
1484
1504
|
if ($props.border.color) { $border.Color = Color-Value ([string]$props.border.color) }
|
|
1485
1505
|
}
|
|
1486
|
-
if ($props.listKind) {
|
|
1487
|
-
$kind = ([string]$props.listKind).ToLowerInvariant()
|
|
1488
|
-
if ($kind -eq 'number') {
|
|
1489
|
-
$paragraph.Range.ListFormat.ApplyNumberDefault()
|
|
1490
|
-
} elseif ($kind -ne 'none') {
|
|
1491
|
-
$paragraph.Range.ListFormat.ApplyBulletDefault()
|
|
1492
|
-
} else {
|
|
1493
|
-
$paragraph.Range.ListFormat.RemoveNumbers()
|
|
1494
|
-
}
|
|
1495
|
-
for ($level = 0; $level -lt [int]$props.listLevel; $level++) { $paragraph.Range.ListFormat.ListIndent() }
|
|
1496
|
-
} else {
|
|
1497
|
-
$paragraph.Range.ListFormat.RemoveNumbers()
|
|
1498
|
-
}
|
|
1499
|
-
$paragraphIndex = if ($reusedInitialParagraph) { 1 } else { [Math]::Max(1, [int]$doc.Paragraphs.Count - 1) }
|
|
1500
1506
|
return [ordered]@{ op = 'append_text'; changed = $true; paragraph = $paragraphIndex; path = "/body/p[$paragraphIndex]"; style = $style }
|
|
1501
1507
|
}
|
|
1502
1508
|
'set_table_cell' {
|
|
@@ -1741,7 +1747,9 @@ function Apply-WordOperation($doc, $op) {
|
|
|
1741
1747
|
default { 1 }
|
|
1742
1748
|
}
|
|
1743
1749
|
$collection = if ($null -ne $op.header -and -not [bool]$op.header) { $section.Footers } else { $section.Headers }
|
|
1744
|
-
$collection.Item($kind)
|
|
1750
|
+
$item = $collection.Item($kind)
|
|
1751
|
+
$item.Exists = $true
|
|
1752
|
+
$item.Range.Text = [string]$op.text
|
|
1745
1753
|
return [ordered]@{ op = 'set_header_footer'; changed = $true; section = $section.Index }
|
|
1746
1754
|
}
|
|
1747
1755
|
'track_changes' {
|
|
@@ -1780,24 +1788,43 @@ function Apply-WordOperation($doc, $op) {
|
|
|
1780
1788
|
}
|
|
1781
1789
|
'add_page_numbers' {
|
|
1782
1790
|
$section = $doc.Sections.Item($(if ($op.section) { [int]$op.section } else { 1 }))
|
|
1791
|
+
if (-not $op.kind) {
|
|
1792
|
+
$section.PageSetup.DifferentFirstPageHeaderFooter = 0
|
|
1793
|
+
$section.PageSetup.OddAndEvenPagesHeaderFooter = 0
|
|
1794
|
+
}
|
|
1783
1795
|
$kind = switch ([string]$op.kind) {
|
|
1784
1796
|
'first' { 2 }
|
|
1785
1797
|
'even' { 3 }
|
|
1786
1798
|
default { 1 }
|
|
1787
1799
|
}
|
|
1788
|
-
$
|
|
1789
|
-
$
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
$range.Collapse(0)
|
|
1793
|
-
$null = $footer.Range.Fields.Add($range, -1, 'PAGE', $true)
|
|
1794
|
-
if ($null -eq $op.includeTotal -or [bool]$op.includeTotal) {
|
|
1800
|
+
$footerKinds = if ($op.kind) { @($kind) } else { @(1, 2, 3) }
|
|
1801
|
+
foreach ($footerKind in $footerKinds) {
|
|
1802
|
+
$footer = $section.Footers.Item($footerKind)
|
|
1803
|
+
$footer.Exists = $true
|
|
1795
1804
|
$range = $footer.Range
|
|
1796
|
-
$range.
|
|
1797
|
-
$range.InsertAfter($(if ($null -ne $op.separator) { [string]$op.separator } else { ' of ' }))
|
|
1805
|
+
$range.Text = $(if ($null -ne $op.prefix) { [string]$op.prefix } else { 'Page ' })
|
|
1798
1806
|
$range = $footer.Range
|
|
1799
1807
|
$range.Collapse(0)
|
|
1800
|
-
$null = $footer.Range.Fields.Add($range, -1, '
|
|
1808
|
+
$null = $footer.Range.Fields.Add($range, -1, 'PAGE', $true)
|
|
1809
|
+
if ($null -eq $op.includeTotal -or [bool]$op.includeTotal) {
|
|
1810
|
+
$range = $footer.Range
|
|
1811
|
+
$range.Collapse(0)
|
|
1812
|
+
$range.InsertAfter($(if ($null -ne $op.separator) { [string]$op.separator } else { ' of ' }))
|
|
1813
|
+
$range = $footer.Range
|
|
1814
|
+
$range.Collapse(0)
|
|
1815
|
+
$null = $footer.Range.Fields.Add($range, -1, 'NUMPAGES', $true)
|
|
1816
|
+
}
|
|
1817
|
+
$footer.Range.ParagraphFormat.Alignment = switch (([string]$op.alignment).ToLowerInvariant()) {
|
|
1818
|
+
'left' { 0 }
|
|
1819
|
+
'right' { 2 }
|
|
1820
|
+
default { 1 }
|
|
1821
|
+
}
|
|
1822
|
+
try {
|
|
1823
|
+
$normalFont = $doc.Styles.Item(-1).Font
|
|
1824
|
+
if ($normalFont.Name) { $footer.Range.Font.Name = [string]$normalFont.Name }
|
|
1825
|
+
if ($normalFont.NameFarEast) { $footer.Range.Font.NameFarEast = [string]$normalFont.NameFarEast }
|
|
1826
|
+
$footer.Range.Font.Size = 9
|
|
1827
|
+
} catch {}
|
|
1801
1828
|
}
|
|
1802
1829
|
return [ordered]@{ op = 'add_page_numbers'; changed = $true; section = [int]$section.Index }
|
|
1803
1830
|
}
|
|
@@ -2134,10 +2161,62 @@ function Apply-ExcelOperation($book, $op) {
|
|
|
2134
2161
|
$width = if ($op.width) { [single]$op.width } else { [single]480 }
|
|
2135
2162
|
$height = if ($op.height) { [single]$op.height } else { [single]280 }
|
|
2136
2163
|
$shape = $sheet.Shapes.AddChart2(-1, $chartType, $left, $top, $width, $height)
|
|
2137
|
-
|
|
2138
|
-
if ($op.
|
|
2139
|
-
$
|
|
2140
|
-
$
|
|
2164
|
+
$chart = $shape.Chart
|
|
2165
|
+
if ($op.range) { $chart.SetSourceData($sheet.Range([string]$op.range), 2) }
|
|
2166
|
+
if ($op.title) { $chart.HasTitle = $true; $chart.ChartTitle.Text = [string]$op.title }
|
|
2167
|
+
$seriesCount = [int]$chart.SeriesCollection().Count
|
|
2168
|
+
$palette = @($op.seriesColors)
|
|
2169
|
+
for ($seriesIndex = 1; $seriesIndex -le $seriesCount; $seriesIndex++) {
|
|
2170
|
+
$series = $chart.SeriesCollection().Item($seriesIndex)
|
|
2171
|
+
if ($palette.Count -gt 0) {
|
|
2172
|
+
$color = Color-Value ([string]$palette[($seriesIndex - 1) % $palette.Count])
|
|
2173
|
+
try { $series.Format.Fill.Solid(); $series.Format.Fill.ForeColor.RGB = $color } catch {}
|
|
2174
|
+
try { $series.Format.Line.ForeColor.RGB = $color } catch {}
|
|
2175
|
+
}
|
|
2176
|
+
if ([bool]$op.showValues) {
|
|
2177
|
+
try {
|
|
2178
|
+
$series.ApplyDataLabels()
|
|
2179
|
+
$labels = $series.DataLabels()
|
|
2180
|
+
$labels.ShowValue = $true
|
|
2181
|
+
$labels.ShowCategoryName = $false
|
|
2182
|
+
if ($op.valueNumberFormat) { $labels.NumberFormat = [string]$op.valueNumberFormat }
|
|
2183
|
+
if ($op.dataLabelPosition) {
|
|
2184
|
+
$labels.Position = switch (([string]$op.dataLabelPosition).ToLowerInvariant()) {
|
|
2185
|
+
'center' { -4108 }
|
|
2186
|
+
'inside_base' { 4 }
|
|
2187
|
+
'inside_end' { 3 }
|
|
2188
|
+
'outside_end' { 2 }
|
|
2189
|
+
default { 5 }
|
|
2190
|
+
}
|
|
2191
|
+
}
|
|
2192
|
+
if ($op.dataLabelColor) { $labels.Font.Color = Color-Value ([string]$op.dataLabelColor) }
|
|
2193
|
+
} catch {}
|
|
2194
|
+
}
|
|
2195
|
+
}
|
|
2196
|
+
$chart.HasLegend = $(if ($null -ne $op.showLegend) { [bool]$op.showLegend } else { $seriesCount -gt 1 })
|
|
2197
|
+
try { $chart.ChartArea.Format.Line.Visible = 0 } catch {}
|
|
2198
|
+
try { $chart.PlotArea.Format.Fill.Visible = 0 } catch {}
|
|
2199
|
+
try {
|
|
2200
|
+
$categoryAxis = $chart.Axes(1, 1)
|
|
2201
|
+
$categoryAxis.CategoryType = 2
|
|
2202
|
+
$categoryAxis.TickLabelPosition = 4
|
|
2203
|
+
$categoryAxis.TickLabels.Orientation = 0
|
|
2204
|
+
} catch {}
|
|
2205
|
+
try {
|
|
2206
|
+
$valueAxis = $chart.Axes(2, 1)
|
|
2207
|
+
if ([bool]$op.zeroBaseline) { $valueAxis.MinimumScale = 0 }
|
|
2208
|
+
if ($op.valueNumberFormat) { $valueAxis.TickLabels.NumberFormat = [string]$op.valueNumberFormat }
|
|
2209
|
+
} catch {}
|
|
2210
|
+
try { $chart.ChartGroups(1).GapWidth = 90 } catch {}
|
|
2211
|
+
if ([bool]$op.showValues -and ([string]$op.dataLabelPosition).ToLowerInvariant() -eq 'inside_end') {
|
|
2212
|
+
try {
|
|
2213
|
+
$currentInsideWidth = [single]$chart.PlotArea.InsideWidth
|
|
2214
|
+
if ($currentInsideWidth -gt 100) {
|
|
2215
|
+
$chart.PlotArea.InsideWidth = [single]($currentInsideWidth - 18)
|
|
2216
|
+
}
|
|
2217
|
+
} catch {}
|
|
2218
|
+
}
|
|
2219
|
+
$pointCount = if ($seriesCount -gt 0) { [int]$chart.SeriesCollection().Item(1).Points().Count } else { 0 }
|
|
2141
2220
|
return [ordered]@{ op = 'add_chart'; changed = $true; name = [string]$shape.Name; series = $seriesCount; categories = $pointCount }
|
|
2142
2221
|
}
|
|
2143
2222
|
'add_conditional_format' {
|
|
@@ -2283,6 +2362,7 @@ function Apply-ExcelOperation($book, $op) {
|
|
|
2283
2362
|
if ($null -ne $op.fitToPagesWide) { $setup.FitToPagesWide = [int]$op.fitToPagesWide }
|
|
2284
2363
|
if ($null -ne $op.fitToPagesTall) { $setup.FitToPagesTall = [int]$op.fitToPagesTall }
|
|
2285
2364
|
if ($null -ne $op.centerHorizontally) { $setup.CenterHorizontally = [bool]$op.centerHorizontally }
|
|
2365
|
+
if ($null -ne $op.centerVertically) { $setup.CenterVertically = [bool]$op.centerVertically }
|
|
2286
2366
|
if ($null -ne $op.topMargin) { $setup.TopMargin = [double]$op.topMargin }
|
|
2287
2367
|
if ($null -ne $op.bottomMargin) { $setup.BottomMargin = [double]$op.bottomMargin }
|
|
2288
2368
|
if ($null -ne $op.leftMargin) { $setup.LeftMargin = [double]$op.leftMargin }
|
|
@@ -2360,23 +2440,23 @@ function Set-PowerPointChartData($chart, $categoryValues, $seriesValues) {
|
|
|
2360
2440
|
$null = ($source.Value2 = $matrix)
|
|
2361
2441
|
$collection = $chart.SeriesCollection()
|
|
2362
2442
|
$placeholderSeriesCount = [int]$collection.Count
|
|
2363
|
-
$
|
|
2364
|
-
|
|
2365
|
-
$categoryArray[$pointIndex] = if ($pointIndex -lt $categories.Count) { $categories[$pointIndex] } else { "Item $($pointIndex + 1)" }
|
|
2366
|
-
}
|
|
2443
|
+
$sheetName = ([string]$worksheet.Name).Replace("'", "''")
|
|
2444
|
+
$lastRow = $pointCount + 1
|
|
2367
2445
|
for ($seriesIndex = 1; $seriesIndex -le $specs.Count; $seriesIndex++) {
|
|
2368
2446
|
$series = $collection.NewSeries()
|
|
2369
|
-
$
|
|
2370
|
-
$valueArray = New-Object 'object[]' $pointCount
|
|
2371
|
-
for ($pointIndex = 0; $pointIndex -lt $pointCount; $pointIndex++) {
|
|
2372
|
-
if ($pointIndex -lt $values.Count) { $valueArray[$pointIndex] = $values[$pointIndex] }
|
|
2373
|
-
}
|
|
2447
|
+
$column = Excel-ColumnLabel ($seriesIndex + 1)
|
|
2374
2448
|
$null = ($series.Name = [string]$specs[$seriesIndex - 1].name)
|
|
2375
|
-
$null = ($series.XValues = $
|
|
2376
|
-
$null = ($series.Values = $
|
|
2449
|
+
$null = ($series.XValues = "='$sheetName'!`$A`$2:`$A`$$lastRow")
|
|
2450
|
+
$null = ($series.Values = "='$sheetName'!`$$column`$2:`$$column`$$lastRow")
|
|
2377
2451
|
}
|
|
2378
2452
|
for ($seriesIndex = 0; $seriesIndex -lt $placeholderSeriesCount; $seriesIndex++) { $null = $collection.Item(1).Delete() }
|
|
2379
2453
|
$null = $chart.Refresh()
|
|
2454
|
+
try {
|
|
2455
|
+
$categoryAxis = $chart.Axes(1, 1)
|
|
2456
|
+
$categoryAxis.CategoryType = 2
|
|
2457
|
+
$categoryAxis.TickLabelPosition = 4
|
|
2458
|
+
$categoryAxis.TickLabels.Orientation = 0
|
|
2459
|
+
} catch {}
|
|
2380
2460
|
} finally {
|
|
2381
2461
|
if ($null -ne $workbook) { try { $null = $workbook.Close($true) } catch {} }
|
|
2382
2462
|
if ($null -ne $worksheet) { try { [void][System.Runtime.InteropServices.Marshal]::ReleaseComObject($worksheet) } catch {} }
|
|
@@ -3400,6 +3480,8 @@ function Issues-PowerPoint($presentation, $payload) {
|
|
|
3400
3480
|
$shapeIndex = 0
|
|
3401
3481
|
$textShapeCount = 0
|
|
3402
3482
|
$visualShapeCount = 0
|
|
3483
|
+
$largeTextShapeCount = 0
|
|
3484
|
+
$dominantTextShapeCount = 0
|
|
3403
3485
|
foreach ($shape in @($slide.Shapes)) {
|
|
3404
3486
|
$shapeIndex++
|
|
3405
3487
|
$path = "/slide[$($slide.SlideIndex)]/shape[$shapeIndex]"
|
|
@@ -3422,6 +3504,8 @@ function Issues-PowerPoint($presentation, $payload) {
|
|
|
3422
3504
|
$fontIssue = Missing-FontIssue $fonts ([string]$shape.TextFrame.TextRange.Font.Name) $path
|
|
3423
3505
|
if ($fontIssue) { $issues += $fontIssue }
|
|
3424
3506
|
$fontSize = [single]$shape.TextFrame.TextRange.Font.Size
|
|
3507
|
+
if ($fontSize -ge 34) { $largeTextShapeCount++ }
|
|
3508
|
+
if ($fontSize -ge 42) { $dominantTextShapeCount++ }
|
|
3425
3509
|
if ($fontSize -gt 0 -and $fontSize -lt 12) {
|
|
3426
3510
|
$issues += Office-Issue 'warning' 'small_font' $path "Text uses $fontSize pt; presentation body text should normally be at least 12 pt."
|
|
3427
3511
|
}
|
|
@@ -3481,7 +3565,9 @@ function Issues-PowerPoint($presentation, $payload) {
|
|
|
3481
3565
|
} catch {}
|
|
3482
3566
|
}
|
|
3483
3567
|
}
|
|
3484
|
-
|
|
3568
|
+
$typographicVisual = $dominantTextShapeCount -gt 0 -or $largeTextShapeCount -ge 2
|
|
3569
|
+
$boundarySlide = [int]$slide.SlideIndex -eq 1 -or [int]$slide.SlideIndex -eq [int]$presentation.Slides.Count
|
|
3570
|
+
if ($textShapeCount -gt 0 -and $visualShapeCount -eq 0 -and -not $typographicVisual -and -not $boundarySlide) {
|
|
3485
3571
|
$issues += Office-Issue 'warning' 'text_only_slide' "/slide[$($slide.SlideIndex)]" 'Slide contains text but no visual shape, chart, table, diagram, or picture.'
|
|
3486
3572
|
}
|
|
3487
3573
|
if ($payload.auditProfile -eq 'model-backed-deck') {
|