@dsz-examaware/plugin-demo 0.1.1
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/LICENSE +674 -0
- package/README.md +27 -0
- package/dist/main/index.cjs +538 -0
- package/dist/main/index.cjs.map +1 -0
- package/dist/main/index.d.ts +2 -0
- package/dist/renderer/assets/examaware-plugin-template.css +43 -0
- package/dist/renderer/components/PluginSettingsPage.vue.d.ts +7 -0
- package/dist/renderer/index.mjs +318 -0
- package/dist/renderer/index.mjs.map +1 -0
- package/dist/renderer/main.d.ts +2 -0
- package/env.d.ts +7 -0
- package/package.json +51 -0
- package/schema.json +21 -0
- package/src/main/index.ts +54 -0
- package/src/renderer/components/PluginSettingsPage.vue +9 -0
- package/src/renderer/main.ts +34 -0
- package/tsconfig.json +15 -0
- package/tsconfig.node.json +10 -0
- package/vite.config.ts +51 -0
- package/vite.main.config.ts +37 -0
package/env.d.ts
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dsz-examaware/plugin-demo",
|
|
3
|
+
"version": "0.1.1",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./dist/main/index.cjs",
|
|
7
|
+
"module": "./dist/renderer/index.mjs",
|
|
8
|
+
"types": "./dist/main/index.d.ts",
|
|
9
|
+
"examaware": {
|
|
10
|
+
"displayName": "ExamAware Demo Plugin",
|
|
11
|
+
"description": "A starter plugin showing main + renderer wiring with a settings page.",
|
|
12
|
+
"targets": {
|
|
13
|
+
"main": "./dist/main/index.cjs",
|
|
14
|
+
"renderer": "./dist/renderer/index.mjs"
|
|
15
|
+
},
|
|
16
|
+
"services": {
|
|
17
|
+
"provide": [
|
|
18
|
+
"hello.message"
|
|
19
|
+
],
|
|
20
|
+
"inject": []
|
|
21
|
+
},
|
|
22
|
+
"settings": {
|
|
23
|
+
"namespace": "examaware-plugin-template",
|
|
24
|
+
"schema": "./schema.json"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"dependencies": {
|
|
28
|
+
"vue": "^3.5.26",
|
|
29
|
+
"@dsz-examaware/plugin-sdk": "^1.1.1"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/node": "^20.19.27",
|
|
33
|
+
"@vitejs/plugin-vue": "^5.2.4",
|
|
34
|
+
"npm-run-all2": "^7.0.2",
|
|
35
|
+
"run-p": "0.0.0",
|
|
36
|
+
"typescript": "~5.7.3",
|
|
37
|
+
"vite": "^6.4.1",
|
|
38
|
+
"vue-tsc": "^2.2.12"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"dev": "run-p dev:renderer dev:main",
|
|
42
|
+
"dev:renderer": "vite build --watch --mode development --config vite.config.ts",
|
|
43
|
+
"dev:main": "vite build --watch --mode development --config vite.main.config.ts",
|
|
44
|
+
"build": "pnpm run build:renderer && pnpm run build:main && pnpm run build:types",
|
|
45
|
+
"build:renderer": "vite build --config vite.config.ts",
|
|
46
|
+
"build:main": "vite build --config vite.main.config.ts",
|
|
47
|
+
"build:types": "vue-tsc --declaration --emitDeclarationOnly --outDir dist",
|
|
48
|
+
"pack": "pnpm run build && pack-examaware-plugin",
|
|
49
|
+
"lint": "vue-tsc --noEmit"
|
|
50
|
+
}
|
|
51
|
+
}
|
package/schema.json
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "object",
|
|
3
|
+
"properties": {
|
|
4
|
+
"demo": {
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"clicks": {
|
|
8
|
+
"type": "integer",
|
|
9
|
+
"minimum": 0,
|
|
10
|
+
"default": 0,
|
|
11
|
+
"description": "Number of times the demo button was clicked."
|
|
12
|
+
},
|
|
13
|
+
"message": {
|
|
14
|
+
"type": "string",
|
|
15
|
+
"default": "Hello from ExamAware Demo Plugin",
|
|
16
|
+
"description": "Message shown in main-process heartbeat logs."
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { defineExamAwarePlugin } from '@dsz-examaware/plugin-sdk';
|
|
2
|
+
import type { HostedService } from '@dsz-examaware/plugin-sdk';
|
|
3
|
+
|
|
4
|
+
interface HelloMessage {
|
|
5
|
+
text: string;
|
|
6
|
+
timestamp: number;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
class HeartbeatService implements HostedService {
|
|
10
|
+
constructor(private readonly message: HelloMessage) {}
|
|
11
|
+
|
|
12
|
+
private interval?: ReturnType<typeof setInterval>;
|
|
13
|
+
|
|
14
|
+
async start() {
|
|
15
|
+
this.interval = setInterval(() => {
|
|
16
|
+
console.info('[examaware-plugin-template]', this.message.text, new Date().toISOString());
|
|
17
|
+
}, 10_000);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async stop() {
|
|
21
|
+
if (this.interval) clearInterval(this.interval);
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export default defineExamAwarePlugin((builder) => {
|
|
26
|
+
builder.configureServices((context, services) => {
|
|
27
|
+
services.addSingleton('hello.message', () => ({
|
|
28
|
+
text: context.ctx.config?.message ?? 'Hello from ExamAware Demo Plugin1',
|
|
29
|
+
timestamp: Date.now()
|
|
30
|
+
}));
|
|
31
|
+
|
|
32
|
+
// Register both class and string tokens to avoid identity mismatches
|
|
33
|
+
services.addSingleton(HeartbeatService, (sp) => new HeartbeatService(sp.get('hello.message')));
|
|
34
|
+
services.tryAddSingleton('heartbeat.service', (sp) => sp.get(HeartbeatService));
|
|
35
|
+
context.ctx.logger.info(
|
|
36
|
+
'[examaware-plugin-template] registered HeartbeatService/heartbeat.service'
|
|
37
|
+
);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
builder.exposeHostService('hello.message', { token: 'hello.message' });
|
|
41
|
+
builder.addHostedService('heartbeat.service');
|
|
42
|
+
|
|
43
|
+
builder.use(async ({ ctx }, next) => {
|
|
44
|
+
ctx.logger.info('Plugin boot sequence started');
|
|
45
|
+
await next();
|
|
46
|
+
ctx.logger.info('Plugin boot sequence completed');
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
builder.configure((host, app) => {
|
|
50
|
+
host.lifetime.onStarted(() => {
|
|
51
|
+
app.ctx.logger.info('Host lifetime onStarted hook invoked');
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
});
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { defineComponent, h } from 'vue';
|
|
2
|
+
import type { PluginRuntimeContext } from '@dsz-examaware/plugin-sdk';
|
|
3
|
+
import PluginSettingsPage from './components/PluginSettingsPage.vue';
|
|
4
|
+
|
|
5
|
+
const SETTINGS_PAGE_ID = 'examaware-plugin-template-settings';
|
|
6
|
+
|
|
7
|
+
export default async function setupRenderer(ctx: PluginRuntimeContext) {
|
|
8
|
+
if (ctx.app !== 'renderer') return;
|
|
9
|
+
const desktopApi = ctx.desktopApi as any;
|
|
10
|
+
const settingsUi = desktopApi?.ui?.settings;
|
|
11
|
+
if (!settingsUi) {
|
|
12
|
+
ctx.logger.warn('Desktop API does not expose settings UI in this build.');
|
|
13
|
+
return;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const SettingsEntry = defineComponent({
|
|
17
|
+
name: 'ExamAwarePluginSettingsEntry',
|
|
18
|
+
setup() {
|
|
19
|
+
return () => h(PluginSettingsPage, { ctx });
|
|
20
|
+
}
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const handle = await settingsUi.registerPage({
|
|
24
|
+
id: SETTINGS_PAGE_ID,
|
|
25
|
+
label: 'Plugin Settings',
|
|
26
|
+
icon: 'extension',
|
|
27
|
+
order: 50,
|
|
28
|
+
component: () => Promise.resolve(SettingsEntry)
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
if (handle) {
|
|
32
|
+
ctx.effect(() => () => handle.dispose());
|
|
33
|
+
}
|
|
34
|
+
}
|
package/tsconfig.json
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
{
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "ES2020",
|
|
4
|
+
"module": "ESNext",
|
|
5
|
+
"moduleResolution": "bundler",
|
|
6
|
+
"allowImportingTsExtensions": true,
|
|
7
|
+
"allowSyntheticDefaultImports": true,
|
|
8
|
+
"strict": true,
|
|
9
|
+
"esModuleInterop": true,
|
|
10
|
+
"skipLibCheck": true,
|
|
11
|
+
"types": ["node", "vite/client"]
|
|
12
|
+
},
|
|
13
|
+
"include": ["src/**/*.ts", "src/**/*.d.ts", "src/**/*.vue", "env.d.ts"],
|
|
14
|
+
"references": [{ "path": "./tsconfig.node.json" }]
|
|
15
|
+
}
|
package/vite.config.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { resolve } from 'node:path';
|
|
2
|
+
import { defineConfig } from 'vite';
|
|
3
|
+
import type { ResolvedConfig } from 'vite';
|
|
4
|
+
import vue from '@vitejs/plugin-vue';
|
|
5
|
+
|
|
6
|
+
const inlineAllDeps = () => ({
|
|
7
|
+
name: 'inline-all-deps',
|
|
8
|
+
configResolved(config: ResolvedConfig) {
|
|
9
|
+
if (!config?.build?.rollupOptions) return;
|
|
10
|
+
config.build.rollupOptions.external = undefined;
|
|
11
|
+
}
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
export default defineConfig(({ mode }) => {
|
|
15
|
+
const isProd = mode === 'production';
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
define: {
|
|
19
|
+
__DEV__: !isProd,
|
|
20
|
+
'process.env.NODE_ENV': JSON.stringify(isProd ? 'production' : 'development'),
|
|
21
|
+
'process.env': JSON.stringify({ NODE_ENV: isProd ? 'production' : 'development' }),
|
|
22
|
+
process: JSON.stringify({ env: { NODE_ENV: isProd ? 'production' : 'development' } })
|
|
23
|
+
},
|
|
24
|
+
plugins: [vue(), inlineAllDeps()],
|
|
25
|
+
base: './',
|
|
26
|
+
build: {
|
|
27
|
+
emptyOutDir: false,
|
|
28
|
+
outDir: 'dist/renderer',
|
|
29
|
+
sourcemap: !isProd,
|
|
30
|
+
minify: isProd ? 'esbuild' : false,
|
|
31
|
+
cssCodeSplit: false,
|
|
32
|
+
target: 'esnext',
|
|
33
|
+
watch: isProd ? undefined : {},
|
|
34
|
+
lib: {
|
|
35
|
+
entry: resolve(__dirname, 'src/renderer/main.ts'),
|
|
36
|
+
fileName: () => 'index.mjs',
|
|
37
|
+
formats: ['es'],
|
|
38
|
+
name: 'ExamAwareRendererPlugin'
|
|
39
|
+
},
|
|
40
|
+
rollupOptions: {
|
|
41
|
+
output: {
|
|
42
|
+
format: 'es',
|
|
43
|
+
entryFileNames: 'index.mjs',
|
|
44
|
+
inlineDynamicImports: true,
|
|
45
|
+
chunkFileNames: 'chunks/[name].js',
|
|
46
|
+
assetFileNames: 'assets/[name][extname]'
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
});
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { builtinModules } from 'node:module';
|
|
2
|
+
import { resolve } from 'node:path';
|
|
3
|
+
import { defineConfig } from 'vite';
|
|
4
|
+
|
|
5
|
+
const builtins = new Set<string>([
|
|
6
|
+
...builtinModules,
|
|
7
|
+
...builtinModules.map((mod) => `node:${mod}`)
|
|
8
|
+
]);
|
|
9
|
+
// Bundle plugin-sdk; only externalize Electron + Node builtins so the plugin is self-contained.
|
|
10
|
+
const externalDeps = ['electron', 'electron/main', ...builtins];
|
|
11
|
+
|
|
12
|
+
export default defineConfig(({ mode }) => {
|
|
13
|
+
const isProd = mode === 'production';
|
|
14
|
+
|
|
15
|
+
return {
|
|
16
|
+
build: {
|
|
17
|
+
lib: {
|
|
18
|
+
entry: resolve(__dirname, 'src/main/index.ts'),
|
|
19
|
+
fileName: () => 'index.cjs',
|
|
20
|
+
formats: ['cjs']
|
|
21
|
+
},
|
|
22
|
+
emptyOutDir: false,
|
|
23
|
+
outDir: 'dist/main',
|
|
24
|
+
target: 'node20',
|
|
25
|
+
sourcemap: !isProd,
|
|
26
|
+
minify: false,
|
|
27
|
+
rollupOptions: {
|
|
28
|
+
external: (source) =>
|
|
29
|
+
externalDeps.some((dep) => source === dep || source.startsWith(`${dep}/`)),
|
|
30
|
+
output: {
|
|
31
|
+
exports: 'default',
|
|
32
|
+
esModule: false
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
});
|