@smartcat/sanity-plugin 1.0.0 → 1.0.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/README.md +294 -0
- package/dist/_chunks-cjs/constants.cjs +13 -0
- package/dist/_chunks-cjs/constants.cjs.map +1 -0
- package/dist/_chunks-cjs/index.cjs +168 -0
- package/dist/_chunks-cjs/index.cjs.map +1 -0
- package/dist/_chunks-cjs/index2.cjs +311 -0
- package/dist/_chunks-cjs/index2.cjs.map +1 -0
- package/dist/_chunks-cjs/workflow.cjs +16 -0
- package/dist/_chunks-cjs/workflow.cjs.map +1 -0
- package/dist/_chunks-es/constants.js +14 -0
- package/dist/_chunks-es/constants.js.map +1 -0
- package/dist/_chunks-es/index.js +169 -0
- package/dist/_chunks-es/index.js.map +1 -0
- package/dist/_chunks-es/index2.js +313 -0
- package/dist/_chunks-es/index2.js.map +1 -0
- package/dist/_chunks-es/workflow.js +17 -0
- package/dist/_chunks-es/workflow.js.map +1 -0
- package/dist/export.cjs +160 -0
- package/dist/export.cjs.map +1 -0
- package/dist/export.d.cts +162 -0
- package/dist/export.d.ts +162 -0
- package/dist/export.js +162 -0
- package/dist/export.js.map +1 -0
- package/dist/import.cjs +169 -0
- package/dist/import.cjs.map +1 -0
- package/dist/import.d.cts +155 -0
- package/dist/import.d.ts +155 -0
- package/dist/import.js +169 -0
- package/dist/import.js.map +1 -0
- package/dist/index.cjs +2103 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +122 -0
- package/dist/index.d.ts +122 -0
- package/dist/index.js +2110 -0
- package/dist/index.js.map +1 -0
- package/dist/locjson.cjs +12 -0
- package/dist/locjson.cjs.map +1 -0
- package/dist/locjson.d.cts +287 -0
- package/dist/locjson.d.ts +287 -0
- package/dist/locjson.js +12 -0
- package/dist/locjson.js.map +1 -0
- package/dist/progress.cjs +16 -0
- package/dist/progress.cjs.map +1 -0
- package/dist/progress.d.cts +262 -0
- package/dist/progress.d.ts +262 -0
- package/dist/progress.js +16 -0
- package/dist/progress.js.map +1 -0
- package/dist/smartcat.cjs +287 -0
- package/dist/smartcat.cjs.map +1 -0
- package/dist/smartcat.d.cts +234 -0
- package/dist/smartcat.d.ts +234 -0
- package/dist/smartcat.js +287 -0
- package/dist/smartcat.js.map +1 -0
- package/dist/templates.cjs +12 -0
- package/dist/templates.cjs.map +1 -0
- package/dist/templates.d.cts +52 -0
- package/dist/templates.d.ts +52 -0
- package/dist/templates.js +12 -0
- package/dist/templates.js.map +1 -0
- package/package.json +101 -15
- package/src/actions/AddToProjectAction.tsx +258 -0
- package/src/export/export.test.ts +537 -0
- package/src/export/index.ts +393 -0
- package/src/form/TranslationStatusInput.tsx +212 -0
- package/src/import/import.test.ts +346 -0
- package/src/import/index.ts +418 -0
- package/src/index.ts +63 -0
- package/src/lib/constants.ts +23 -0
- package/src/lib/documentTitle.test.ts +43 -0
- package/src/lib/documentTitle.ts +30 -0
- package/src/lib/languageMap.test.ts +56 -0
- package/src/lib/languageMap.ts +71 -0
- package/src/lib/linkedDocuments.test.ts +56 -0
- package/src/lib/linkedDocuments.ts +100 -0
- package/src/lib/locjson/deserialize.ts +60 -0
- package/src/lib/locjson/fields.ts +355 -0
- package/src/lib/locjson/filename.ts +27 -0
- package/src/lib/locjson/index.ts +10 -0
- package/src/lib/locjson/locjson.test.ts +545 -0
- package/src/lib/locjson/paths.test.ts +41 -0
- package/src/lib/locjson/paths.ts +73 -0
- package/src/lib/locjson/portableText.ts +98 -0
- package/src/lib/locjson/serialize.ts +124 -0
- package/src/lib/locjson/types.ts +106 -0
- package/src/lib/log.ts +33 -0
- package/src/lib/projectItems.ts +17 -0
- package/src/lib/resolveConfig.test.ts +62 -0
- package/src/lib/resolveConfig.ts +47 -0
- package/src/lib/workflow.test.ts +37 -0
- package/src/lib/workflow.ts +48 -0
- package/src/lib/zip.fixtures.ts +75 -0
- package/src/lib/zip.test.ts +110 -0
- package/src/lib/zip.ts +164 -0
- package/src/progress/core.ts +387 -0
- package/src/progress/index.ts +7 -0
- package/src/progress/progress.test.ts +268 -0
- package/src/schema/settings.ts +55 -0
- package/src/schema/translatableOptions.ts +25 -0
- package/src/schema/translationProject.ts +264 -0
- package/src/smartcat/client.test.ts +222 -0
- package/src/smartcat/client.ts +351 -0
- package/src/smartcat/index.ts +10 -0
- package/src/smartcat/types.ts +99 -0
- package/src/templates/core.ts +55 -0
- package/src/templates/index.ts +5 -0
- package/src/templates/templates.test.ts +50 -0
- package/src/tool/Dashboard.tsx +44 -0
- package/src/tool/ItemProgress.tsx +176 -0
- package/src/tool/LogPanel.tsx +207 -0
- package/src/tool/ProjectView.tsx +975 -0
- package/src/tool/ProjectsView.tsx +208 -0
- package/src/tool/StatusBadge.tsx +33 -0
- package/src/tool/StudioInit.tsx +42 -0
- package/src/tool/WorkflowSelect.tsx +41 -0
- package/src/tool/data.ts +79 -0
- package/src/tool/index.ts +19 -0
- package/src/tool/processing.test.ts +623 -0
- package/src/tool/processing.ts +766 -0
- package/src/tool/useTemplates.ts +84 -0
- package/src/tool/useWorkflowSelection.ts +65 -0
- package/src/types.ts +77 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import {useCallback, useEffect, useRef, useState} from 'react'
|
|
2
|
+
import {useClient} from 'sanity'
|
|
3
|
+
import {API_VERSION, SETTINGS_DOC_ID, SETTINGS_TYPE} from '../lib/constants'
|
|
4
|
+
import type {SmartcatTemplate} from '../smartcat/types'
|
|
5
|
+
|
|
6
|
+
interface SettingsDoc {
|
|
7
|
+
templates?: SmartcatTemplate[]
|
|
8
|
+
templatesRequestedAt?: string
|
|
9
|
+
templatesSyncedAt?: string
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const SETTINGS_QUERY = `*[_id == $id][0]{templates, templatesRequestedAt, templatesSyncedAt}`
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* A request older than this is treated as failed/abandoned, so a fresh refresh
|
|
16
|
+
* is allowed — self-heals a sync that never completed (e.g. a function error)
|
|
17
|
+
* instead of wedging the cache in a perpetual "in flight" state.
|
|
18
|
+
*/
|
|
19
|
+
export const REQUEST_STALE_MS = 30_000
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* A refresh is in flight when a request is newer than the last sync *and* recent
|
|
23
|
+
* enough that the function could still be working on it.
|
|
24
|
+
*/
|
|
25
|
+
export function isRefreshInFlight(
|
|
26
|
+
s: {templatesRequestedAt?: string; templatesSyncedAt?: string} | null,
|
|
27
|
+
): boolean {
|
|
28
|
+
const requested = s?.templatesRequestedAt
|
|
29
|
+
if (!requested) return false
|
|
30
|
+
const pending = !s?.templatesSyncedAt || requested > s.templatesSyncedAt
|
|
31
|
+
if (!pending) return false
|
|
32
|
+
return Date.now() - Date.parse(requested) < REQUEST_STALE_MS
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Reads the cached Smartcat templates from the settings singleton and keeps them
|
|
37
|
+
* live via `listen()`. `requestRefresh()` bumps the refresh trigger so the
|
|
38
|
+
* `smartcat-templates` Function re-fetches — skipped while a request is already
|
|
39
|
+
* in flight (debounce). Returns templates sorted by name.
|
|
40
|
+
*/
|
|
41
|
+
export function useTemplates(): {
|
|
42
|
+
templates: SmartcatTemplate[]
|
|
43
|
+
loaded: boolean
|
|
44
|
+
requestRefresh: () => void
|
|
45
|
+
} {
|
|
46
|
+
const client = useClient({apiVersion: API_VERSION})
|
|
47
|
+
const [settings, setSettings] = useState<SettingsDoc | null>(null)
|
|
48
|
+
const [loaded, setLoaded] = useState(false)
|
|
49
|
+
const settingsRef = useRef<SettingsDoc | null>(null)
|
|
50
|
+
settingsRef.current = settings
|
|
51
|
+
|
|
52
|
+
useEffect(() => {
|
|
53
|
+
let cancelled = false
|
|
54
|
+
const load = () =>
|
|
55
|
+
client
|
|
56
|
+
.fetch<SettingsDoc | null>(SETTINGS_QUERY, {id: SETTINGS_DOC_ID})
|
|
57
|
+
.then((s) => {
|
|
58
|
+
if (cancelled) return
|
|
59
|
+
setSettings(s)
|
|
60
|
+
setLoaded(true)
|
|
61
|
+
})
|
|
62
|
+
.catch(() => {})
|
|
63
|
+
load()
|
|
64
|
+
const sub = client
|
|
65
|
+
.listen(SETTINGS_QUERY, {id: SETTINGS_DOC_ID}, {visibility: 'query'})
|
|
66
|
+
.subscribe({next: load, error: () => {}})
|
|
67
|
+
return () => {
|
|
68
|
+
cancelled = true
|
|
69
|
+
sub.unsubscribe()
|
|
70
|
+
}
|
|
71
|
+
}, [client])
|
|
72
|
+
|
|
73
|
+
const requestRefresh = useCallback(() => {
|
|
74
|
+
if (isRefreshInFlight(settingsRef.current)) return
|
|
75
|
+
client
|
|
76
|
+
.transaction()
|
|
77
|
+
.createIfNotExists({_id: SETTINGS_DOC_ID, _type: SETTINGS_TYPE})
|
|
78
|
+
.patch(SETTINGS_DOC_ID, (p) => p.set({templatesRequestedAt: new Date().toISOString()}))
|
|
79
|
+
.commit({visibility: 'async'})
|
|
80
|
+
.catch(() => {})
|
|
81
|
+
}, [client])
|
|
82
|
+
|
|
83
|
+
return {templates: settings?.templates ?? [], loaded, requestRefresh}
|
|
84
|
+
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import {useCallback, useEffect, useMemo, useRef, useState} from 'react'
|
|
2
|
+
import {isWorkflowValid} from '../lib/workflow'
|
|
3
|
+
import type {SmartcatTemplate} from '../smartcat/types'
|
|
4
|
+
|
|
5
|
+
const STORAGE_KEY = 'smartcat:lastWorkflow'
|
|
6
|
+
|
|
7
|
+
function readLastWorkflow(): string {
|
|
8
|
+
try {
|
|
9
|
+
return localStorage.getItem(STORAGE_KEY) ?? ''
|
|
10
|
+
} catch {
|
|
11
|
+
return ''
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function writeLastWorkflow(value: string): void {
|
|
16
|
+
try {
|
|
17
|
+
localStorage.setItem(STORAGE_KEY, value)
|
|
18
|
+
} catch {
|
|
19
|
+
// Ignore unavailable / full storage; selection just won't persist.
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Workflow selection for a create dialog. Starts with **no selection** so the
|
|
25
|
+
* user must choose, then remembers the last pick per browser (localStorage).
|
|
26
|
+
*
|
|
27
|
+
* When `active` (the dialog is open) and the template cache is `loaded`, it
|
|
28
|
+
* prefills the saved value — but only if it's still valid: a `template:<id>`
|
|
29
|
+
* whose template no longer exists resets to no selection. It also clears the
|
|
30
|
+
* selection live if the chosen template later disappears from the cache.
|
|
31
|
+
*/
|
|
32
|
+
export function useWorkflowSelection(
|
|
33
|
+
active: boolean,
|
|
34
|
+
templates: SmartcatTemplate[],
|
|
35
|
+
loaded: boolean,
|
|
36
|
+
): readonly [string, (value: string) => void] {
|
|
37
|
+
const [workflow, setWorkflowState] = useState('')
|
|
38
|
+
const initialized = useRef(false)
|
|
39
|
+
const templateIds = useMemo(() => templates.map((t) => t.id), [templates])
|
|
40
|
+
|
|
41
|
+
// Prefill the saved selection once per open, after the cache has loaded.
|
|
42
|
+
useEffect(() => {
|
|
43
|
+
if (!active) {
|
|
44
|
+
initialized.current = false
|
|
45
|
+
return
|
|
46
|
+
}
|
|
47
|
+
if (initialized.current || !loaded) return
|
|
48
|
+
initialized.current = true
|
|
49
|
+
const saved = readLastWorkflow()
|
|
50
|
+
setWorkflowState(isWorkflowValid(saved, templateIds) ? saved : '')
|
|
51
|
+
}, [active, loaded, templateIds])
|
|
52
|
+
|
|
53
|
+
// If the selected template disappears from the cache, drop back to no selection.
|
|
54
|
+
useEffect(() => {
|
|
55
|
+
if (!active || !loaded || !workflow) return
|
|
56
|
+
if (!isWorkflowValid(workflow, templateIds)) setWorkflowState('')
|
|
57
|
+
}, [active, loaded, workflow, templateIds])
|
|
58
|
+
|
|
59
|
+
const setWorkflow = useCallback((value: string) => {
|
|
60
|
+
setWorkflowState(value)
|
|
61
|
+
if (value) writeLastWorkflow(value)
|
|
62
|
+
}, [])
|
|
63
|
+
|
|
64
|
+
return [workflow, setWorkflow] as const
|
|
65
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A language an installation can translate into.
|
|
3
|
+
* `id` is the locale tag used in Sanity (and document-internationalization).
|
|
4
|
+
* `smartcatLanguage` is the code Smartcat expects; set it only when the Sanity
|
|
5
|
+
* tag differs from Smartcat's (e.g. id "PL-PL" → smartcatLanguage "pl"). Defaults to `id`.
|
|
6
|
+
*
|
|
7
|
+
* @public
|
|
8
|
+
*/
|
|
9
|
+
export interface SmartcatLanguage {
|
|
10
|
+
id: string
|
|
11
|
+
title: string
|
|
12
|
+
smartcatLanguage?: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Plugin configuration, supplied by the consuming Studio.
|
|
17
|
+
*
|
|
18
|
+
* @public
|
|
19
|
+
*/
|
|
20
|
+
export interface SmartcatPluginConfig {
|
|
21
|
+
/**
|
|
22
|
+
* Document types the "Add to translation project" action and status line
|
|
23
|
+
* appear on. Omit to allow every document type (except the plugin's own
|
|
24
|
+
* `smartcat.*` types). This affects the Studio UI only — not which linked
|
|
25
|
+
* documents get pulled into a project (see `rootTypes`).
|
|
26
|
+
*/
|
|
27
|
+
translatableTypes?: string[]
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Root/standalone document types — the "include linked documents" crawl stops
|
|
31
|
+
* when it reaches one (they're translated on their own, as peers). Omit to
|
|
32
|
+
* inherit the explicitly-configured `translatableTypes`, or `[]` if that is
|
|
33
|
+
* also omitted (i.e. no boundary: pull in every transitively-linked
|
|
34
|
+
* translatable document).
|
|
35
|
+
*/
|
|
36
|
+
rootTypes?: string[]
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Source language tag for new projects. Required.
|
|
40
|
+
*/
|
|
41
|
+
sourceLanguage: string
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Languages content can be translated into. Used to populate
|
|
45
|
+
* a project's target languages. Optional at this phase.
|
|
46
|
+
*/
|
|
47
|
+
languages?: SmartcatLanguage[]
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Field name `@sanity/document-internationalization` stores each document's
|
|
51
|
+
* locale in. Must match that plugin's `languageField` setting. Defaults to
|
|
52
|
+
* `'language'`.
|
|
53
|
+
*/
|
|
54
|
+
documentI18nPluginLanguageField?: string
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* How `sanity-plugin-internationalized-array` identifies the locale within an
|
|
58
|
+
* array field's members. `'language'` (default) for v5+; set to `'_key'` to
|
|
59
|
+
* support v4.x and lower, which keyed members by the language code.
|
|
60
|
+
*/
|
|
61
|
+
fieldI18nPluginLanguageField?: string
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Lifecycle of a translation project. Drives the export/import Functions.
|
|
66
|
+
*
|
|
67
|
+
* @public
|
|
68
|
+
*/
|
|
69
|
+
export type TranslationProjectStatus =
|
|
70
|
+
| 'draft'
|
|
71
|
+
| 'queued'
|
|
72
|
+
| 'sent'
|
|
73
|
+
| 'translating'
|
|
74
|
+
| 'importing'
|
|
75
|
+
| 'downloaded'
|
|
76
|
+
| 'completed'
|
|
77
|
+
| 'error'
|