diff --git a/.github/instructions/comp-vue.instructions.md b/.github/instructions/comp-vue.instructions.md index da1844f..774f862 100644 --- a/.github/instructions/comp-vue.instructions.md +++ b/.github/instructions/comp-vue.instructions.md @@ -37,6 +37,7 @@ const emit = defineEmits(compNameEmits); - Use `` instead of `

` for inline text - If template is duplicated inside a component and no third one will reuse it, define a reusable template inside the component scope using [VueUse createReusableTemplate](https://vueuse.org/core/createReusableTemplate/), instead of creating a new component. +- Use UnoCSS utility classes for simple styles, instead of writing custom CSS/SCSS. ### Naming diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml new file mode 100644 index 0000000..a8b0d9a --- /dev/null +++ b/.github/workflows/copilot-setup-steps.yml @@ -0,0 +1,36 @@ +name: "Copilot Setup Steps" + +on: + workflow_dispatch: + push: + paths: + - .github/workflows/copilot-setup-steps.yml + pull_request: + paths: + - .github/workflows/copilot-setup-steps.yml + +jobs: + # The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot. + copilot-setup-steps: + runs-on: ubuntu-latest + permissions: + contents: read + packages: read + + # These steps will run before agent starts + steps: + - name: Checkout code + uses: actions/checkout@v5 + + - name: Setup pnpm + uses: pnpm/action-setup@v4 + + - name: Set up Node.js + uses: actions/setup-node@v4 + with: + node-version: "20" + + - name: Install dependencies + run: pnpm install + env: + NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/.gitignore b/.gitignore index 1152f0f..66f511f 100644 --- a/.gitignore +++ b/.gitignore @@ -6,10 +6,14 @@ yarn-debug.log* yarn-error.log* pnpm-debug.log* lerna-debug.log* -pnpm-workspace.yaml +# Dependency directories +pnpm-workspace.yaml node_modules -.DS_Store +pnpm-lock.yaml +package-lock.json + +# Build output dist dist-ssr coverage @@ -31,8 +35,9 @@ coverage *.sln *.sw? +# others +.DS_Store *.tsbuildinfo -pnpm-lock.yaml .__mf__temp # env files diff --git a/.npmrc b/.npmrc index 77862ad..79f2e36 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1,2 @@ -@inkcre:registry=https://npm.pkg.github.com +@inkcre:registry=https://npm.pkg.github.com/ +//npm.pkg.github.com/:_authToken=${NODE_AUTH_TOKEN} \ No newline at end of file diff --git a/AGENTS.md b/AGENTS.md index c6b14d7..a82cbc3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,31 +1,31 @@ # InKCre Client-Web -This is a Vue SPA, provides an interface to interact with InKCre (an information management tool). +InKCre is an information management application aims to provides automatic information collection, organization and powerful use of information. +And this is the Vue implemetation of InKCre, mainly provides a GUI to manage info-base and use information. ## Tech Stacks -- Framework: Vue3 + TypeScript + Sass(scss) + Vite +- Framework: Vue3 + TypeScript + SCSS - Routing: vue-router - Internalization: vue-i18n - Date and time: dayjs - State management: pinia -- Packagae management: pnpm ## Business Domains -- obsrv: Observability -- extension +- source: Data collectors, the input of info-base - info-base - - block - - relation - - resolver - - storage - - source -- sink + - block: Content units + - relation: Links between blocks + - storage: Store block content somewhere else than database. + - resolver: resolves block content +- sink: Interface to use information base, the output of info-base +- extension: extends info-base, source and sink abilities +- obsrv: Observability ## Source Structure -- `components/`: split by domain +- `components/`: split by business domain - `views/` - `business/`: api requests, business logic, split by domain - `stores/`: split by domain @@ -33,10 +33,18 @@ This is a Vue SPA, provides an interface to interact with InKCre (an information - `utils/`: utilities, composables - `locales/`: locale file, split by language - `static/` -- router.ts +- `router.ts` + +## Coding Guidelines + +- Do not repeat yourself: + - Search across the codebase before you creating a new type or something might can be reused. + - Make the code reusable if it's used in over two places. +- [Write code for human](./.github/instructions/human-readable-code.instructions.md) + +## Deployment -## Best Practices +This project supports following deployments: -- Search across the codebase before you creating a new type or something might can be reused. -- Common components should be stateless or avoid maintaining state as much as possible. -- Read more in `.github/instructions/` +- Cloudflare Worker +- Build and serve diff --git a/docs/todo.md b/docs/todo.md index ef3614d..5ad4b69 100644 --- a/docs/todo.md +++ b/docs/todo.md @@ -19,3 +19,5 @@ - [ ] 时区转换有问题 - [x] LogsViewer 的滚动到底部实现有问题 - [x] pending job 不应该 poll logs +- [ ] InkPagination new type +- [ ] Source 不是 info-base 的一部分 diff --git a/package.json b/package.json index fb2c172..ba6bb10 100644 --- a/package.json +++ b/package.json @@ -24,7 +24,7 @@ "@codemirror/lint": "^6.9.2", "@codemirror/state": "^6.5.2", "@codemirror/view": "^6.38.8", - "@inkcre/web-design": "^1.1.1", + "@inkcre/web-design": "^1.1.5", "@module-federation/runtime": "^0.21.4", "@supabase/postgrest-js": "^2.84.0", "@vueuse/core": "^14.0.0", diff --git a/src/business/info-base/source.ts b/src/business/info-base/source.ts index 45309b0..9c66cf5 100644 --- a/src/business/info-base/source.ts +++ b/src/business/info-base/source.ts @@ -170,6 +170,7 @@ export class SourceCollectJob extends Z.class({ .enum(SourceCollectJobStatus) .default(SourceCollectJobStatus.PENDING), state: z.looseObject({}).default(() => ({})), + config: z.looseObject({}).default(() => ({})), }) { static dbApi: DBAPIClient = new DBAPIClient( "sources_collect_jobs", @@ -186,6 +187,46 @@ export class SourceCollectJob extends Z.class({ ); } + static async getBySource( + sourceId: SourceRef, + options?: { + limit?: number; + offset?: number; + order?: "asc" | "desc"; + } + ): Promise<{ data: SourceCollectJob[]; count: number }> { + const { limit = 10, offset = 0, order = "desc" } = options || {}; + const query = this.dbApi + .from() + .select("*", { count: "exact" }) + .eq("source", sourceId) + .order("created_at", { ascending: order === "asc" }) + .range(offset, offset + limit - 1); + + const result = await query; + return { + data: (result.data || []).map((d) => new SourceCollectJob(d)), + count: result.count || 0, + }; + } + + static async getLatestOpenBySource( + sourceId: SourceRef + ): Promise { + const result = await this.dbApi + .from() + .select() + .eq("source", sourceId) + .in("status", [SourceCollectJobStatus.PENDING, SourceCollectJobStatus.RUNNING]) + .order("created_at", { ascending: false }) + .limit(1); + + if (result.data && result.data.length > 0) { + return new SourceCollectJob(result.data[0]); + } + return null; + } + public async getLogs(options?: { limit?: number; cursor?: number; diff --git a/src/components/info-base/source/collectJobForm/collectJobForm.md b/src/components/info-base/source/collectJobForm/collectJobForm.md new file mode 100644 index 0000000..21b1345 --- /dev/null +++ b/src/components/info-base/source/collectJobForm/collectJobForm.md @@ -0,0 +1,26 @@ +# collectJobForm + +## Rationale + +Provides a form interface to configure a source collect job. + +## Goals + +Allow users to configure collect job settings including config object. + +## Specification + +- Pure form component with v-model support +- Provides config editor (InkJsonEditor) +- No create/submit buttons - just form fields +- Parent component handles submission + +## Implementation + +### Props + +- `modelValue` (`SourceCollectJobForm`, required): The form data object + +### Events + +- `update:modelValue(form: SourceCollectJobForm)`: Emitted when form data changes diff --git a/src/components/info-base/source/collectJobForm/collectJobForm.scss b/src/components/info-base/source/collectJobForm/collectJobForm.scss new file mode 100644 index 0000000..a0ba3b0 --- /dev/null +++ b/src/components/info-base/source/collectJobForm/collectJobForm.scss @@ -0,0 +1,5 @@ +.collect-job-form { + display: flex; + flex-direction: column; + gap: sys-var(space, md); +} diff --git a/src/components/info-base/source/collectJobForm/collectJobForm.ts b/src/components/info-base/source/collectJobForm/collectJobForm.ts new file mode 100644 index 0000000..25b6182 --- /dev/null +++ b/src/components/info-base/source/collectJobForm/collectJobForm.ts @@ -0,0 +1,15 @@ +import type { PropType } from "vue"; +import { SourceCollectJobForm } from "@/business/info-base/source"; + +// --- Props --- +export const collectJobFormProps = { + modelValue: { + type: Object as PropType, + required: true, + }, +} as const; + +// --- Emits --- +export const collectJobFormEmits = { + "update:modelValue": (form: SourceCollectJobForm) => true, +} as const; diff --git a/src/components/info-base/source/collectJobForm/collectJobForm.vue b/src/components/info-base/source/collectJobForm/collectJobForm.vue new file mode 100644 index 0000000..545a03e --- /dev/null +++ b/src/components/info-base/source/collectJobForm/collectJobForm.vue @@ -0,0 +1,32 @@ + + + + +