diff --git a/package.json b/package.json index 8709612e0..4f487a1c4 100644 --- a/package.json +++ b/package.json @@ -1,17 +1,19 @@ { "name": "vue-skuilder", "scripts": { - "setup": "yarn && git submodule update --init --recursive", + "setup": "yarn && git submodule update --init --recursive && yarn build:lib", "dev": "yarn workspace @vue-skuilder/common build && yarn workspace @vue-skuilder/db build && yarn workspace @vue-skuilder/common-ui build && yarn workspace @vue-skuilder/courses build && yarn workspace @vue-skuilder/edit-ui build && node scripts/dev-couchdb.js start && concurrently \"yarn dev:platform-ui\" \"yarn dev:express\"", - "dev:platform-ui": "yarn workspace @vue-skuilder/platform-ui dev", - "dev:express": "yarn workspace @vue-skuilder/express dev", - "dev:couchdb": "node scripts/dev-couchdb.js start", + "dev:watch": "node scripts/dev-watch.js", + "dev:platform": "yarn couchdb:start && yarn workspace @vue-skuilder/express dev && yarn workspace @vue-skuilder/platform-ui dev", "couchdb:start": "node scripts/dev-couchdb.js start", "couchdb:stop": "node scripts/dev-couchdb.js stop", "couchdb:status": "node scripts/dev-couchdb.js status", "couchdb:remove": "node scripts/dev-couchdb.js remove", "postdev": "node scripts/dev-couchdb.js stop", "build": "yarn workspace @vue-skuilder/common build && yarn workspace @vue-skuilder/db build && yarn workspace @vue-skuilder/common-ui build && yarn workspace @vue-skuilder/courses build && yarn workspace @vue-skuilder/edit-ui build && yarn workspace @vue-skuilder/platform-ui build && yarn workspace @vue-skuilder/express build", + "build:lib": "yarn workspace @vue-skuilder/common build && yarn workspace @vue-skuilder/db build && yarn workspace @vue-skuilder/common-ui build && yarn workspace @vue-skuilder/courses build && yarn workspace @vue-skuilder/edit-ui build", + "build:pui": "yarn build:lib && yarn workspace @vue-skuilder/platform-ui build && yarn workspace @vue-skuilder/express build", + "build:sui": "yarn build:lib && yarn workspace @vue-skuilder/standalone-ui build", "clean": "yarn clean:dist && yarn clean:node_modules", "clean:dist": "find packages -name 'dist' -type d -exec rm -rf {} +", "clean:node_modules": "find . -name 'node_modules' -type d -exec rm -rf {} +", diff --git a/packages/common-ui/src/stores/useConfigStore.ts b/packages/common-ui/src/stores/useConfigStore.ts index 40e6bad02..9fe0387f1 100644 --- a/packages/common-ui/src/stores/useConfigStore.ts +++ b/packages/common-ui/src/stores/useConfigStore.ts @@ -17,6 +17,7 @@ export const useConfigStore = () => { config: { darkMode: false, likesConfetti: false, + sessionTimeLimit: 5, // Default 5 minutes } as UserConfig, }), @@ -40,6 +41,14 @@ export const useConfigStore = () => { }); }, + async updateSessionTimeLimit(sessionTimeLimit: number) { + this.config.sessionTimeLimit = sessionTimeLimit; + const user = await getCurrentUser(); + await user.setConfig({ + sessionTimeLimit, + }); + }, + async hydrate() { const user = await getCurrentUser(); const cfg = await user.getConfig(); @@ -53,6 +62,7 @@ export const useConfigStore = () => { this.config = { darkMode: false, likesConfetti: false, + sessionTimeLimit: 5, }; }, }, diff --git a/packages/db/src/core/types/user.ts b/packages/db/src/core/types/user.ts index 01a93a122..e314d2759 100644 --- a/packages/db/src/core/types/user.ts +++ b/packages/db/src/core/types/user.ts @@ -4,6 +4,7 @@ import { Moment } from 'moment'; export interface UserConfig { darkMode: boolean; likesConfetti: boolean; + sessionTimeLimit: number; // Session time limit in minutes } export interface ActivityRecord { diff --git a/packages/db/src/impl/common/BaseUserDB.ts b/packages/db/src/impl/common/BaseUserDB.ts index 16ca73f84..05d39d48b 100644 --- a/packages/db/src/impl/common/BaseUserDB.ts +++ b/packages/db/src/impl/common/BaseUserDB.ts @@ -519,6 +519,7 @@ Currently logged-in as ${this._username}.` _id: BaseUser.DOC_IDS.CONFIG, darkMode: false, likesConfetti: false, + sessionTimeLimit: 5, }; try { diff --git a/packages/db/src/util/migrator/StaticToCouchDBMigrator.ts b/packages/db/src/util/migrator/StaticToCouchDBMigrator.ts index 1865a3cc5..284f15fa2 100644 --- a/packages/db/src/util/migrator/StaticToCouchDBMigrator.ts +++ b/packages/db/src/util/migrator/StaticToCouchDBMigrator.ts @@ -425,6 +425,8 @@ export class StaticToCouchDBMigrator { const cleanDoc = { ...doc }; // Remove _rev if present (CouchDB will assign new revision) delete cleanDoc._rev; + // Remove _attachments - these are uploaded separately in Phase 5 + delete cleanDoc._attachments; return cleanDoc; }); @@ -575,10 +577,14 @@ export class StaticToCouchDBMigrator { } } + // Get current document revision (needed for putAttachment) + const doc = await db.get(docId); + // Upload to CouchDB await db.putAttachment( docId, attachmentName, + doc._rev, attachmentData as any, // PouchDB accepts both ArrayBuffer and Buffer attachmentMeta.content_type ); diff --git a/packages/standalone-ui/src/App.vue b/packages/standalone-ui/src/App.vue index 12063c66e..94dea955f 100644 --- a/packages/standalone-ui/src/App.vue +++ b/packages/standalone-ui/src/App.vue @@ -1,5 +1,5 @@ \ No newline at end of file diff --git a/packages/studio-ui/src/views/BrowseView.vue b/packages/studio-ui/src/views/BrowseView.vue index f4d7cfb93..6b7ab4afc 100644 --- a/packages/studio-ui/src/views/BrowseView.vue +++ b/packages/studio-ui/src/views/BrowseView.vue @@ -12,7 +12,7 @@
- +
@@ -36,6 +36,11 @@ const loading = ref(true); const error = ref(null); const courseId = ref(null); +// View lookup function with proper context binding +const viewLookupFunction = (viewDescription: any) => { + return allCourses.getView(viewDescription); +}; + // Initialize browse view onMounted(async () => { try { diff --git a/readme.md b/readme.md index 8d9039a93..bf61e296a 100644 --- a/readme.md +++ b/readme.md @@ -11,15 +11,19 @@ Modular toolkit for the construction of interactive tutoring systems, with exper Think: the FOSS lovechild of Anki, Duolingo, Wikipedia, and MathAcademy, with a more generalized surface area for the types of content and skills that can be exercised. +Also: friendly with local-first and static-deployments. + ## Quick Start -### For Course Creators (building *with* `vue-skuilder`) (start here!) +### For Course Creators (building *with* `vue-skuilder`) (recommmended start point!) -Install the Skuilder CLI to create your first course: +Use the Skuilder CLI to create your first course: ```bash -npm install -g skuilder # npx ok too! +npm install -g skuilder skuilder init my-course --data-layer=static +# Or: +# npx skuilder init my-course --data-layer=static cd my-course npm run dev # serve your course locally npm run studio # edit your course content via web UI @@ -33,10 +37,20 @@ Clone and develop the full platform: git clone https://github.com/patched-network/vue-skuilder.git cd vue-skuilder yarn install -yarn setup # makes git submodule available - test database -yarn dev # runs platform-ui, express API, and CouchDB +yarn setup # makes git submodule available - test database, builds library packages +``` + +After the setup, workflows will differdepending on what you are working on. To explore, I recommend: + +``` +yarn dev:platform ``` +Which will build the components of the platform version of the software and serve locally. +- database: `http://localhost:5984`, with admin accound `admin:password`. +- express backend: `http://localhost:3000` +- platform UI: `http://localhost:5173` + ## Project Architecture This monorepo is structured to support both **platform development** and **course creation**. diff --git a/scripts/dev-watch.js b/scripts/dev-watch.js new file mode 100644 index 000000000..9d40dc43d --- /dev/null +++ b/scripts/dev-watch.js @@ -0,0 +1,53 @@ +const { exec } = require('child_process'); +const fs = require('fs'); +const path = require('path'); + +const packagesDir = path.join(__dirname, '../packages'); +const packages = fs.readdirSync(packagesDir); +const excludedPackages = [ + // legacy / unused pkgs + 'client', + 'e2e-db', + // application packages - only used one at a time, manually started / watched by dev + 'platform-ui', + 'standalone-ui', + 'studio-ui', + 'express', + 'tuilder', + // cli tool - depends on app packages + 'cli', +]; + +const commands = []; +const names = []; + +for (const pkg of packages) { + if (excludedPackages.includes(pkg)) { + continue; + } + const pkgPath = path.join(packagesDir, pkg, 'package.json'); + if (fs.existsSync(pkgPath)) { + const pkgJson = JSON.parse(fs.readFileSync(pkgPath, 'utf-8')); + if (pkgJson.scripts && pkgJson.scripts.dev) { + commands.push(`"yarn workspace ${pkgJson.name} dev"`); + names.push(pkg); + } + } +} + +if (commands.length > 0) { + const concurrentlyCommand = `concurrently -n "${names.join(',')}" ${commands.join(' ')}`; + console.log('Running command:', concurrentlyCommand); + const child = exec(concurrentlyCommand, (error, stdout, stderr) => { + if (error) { + console.error(`exec error: ${error}`); + return; + } + console.log(`stdout: ${stdout}`); + console.error(`stderr: ${stderr}`); + }); + child.stdout.pipe(process.stdout); + child.stderr.pipe(process.stderr); +} else { + console.log('No packages with a "dev" script found.'); +} \ No newline at end of file