PluginProbe
AI / trunk
AI vtrunk
1.3.0 1.2.0 1.1.0 1.0.2 1.0.1 1.0.0 0.9.0 trunk 0.1.1 0.2.0 0.2.1 0.3.0 0.3.1 0.4.0 0.4.1 0.5.0 0.6.0 0.7.0 0.8.0
ai / build-scripts / experiments / content-classification.js

content-classification.js in AI trunk, at build-scripts/experiments/content-classification.js

6 lines 9.2 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 (()=>{"use strict";const t={n:e=>{const n=e&&e.__esModule?()=>e.default:()=>e;return t.d(n,{a:n}),n},d:(e,n)=>{if(Array.isArray(n))for(var i=0;i<n.length;){var s=n[i++],a=n[i++];t.o(e,s)?0===a&&i++:0===a?Object.defineProperty(e,s,{enumerable:!0,value:n[i++]}):Object.defineProperty(e,s,{enumerable:!0,get:a})}else for(var s in n)t.o(n,s)&&!t.o(e,s)&&Object.defineProperty(e,s,{enumerable:!0,get:n[s]})},o:(t,e)=>Object.hasOwn(t,e)},e=window.wp.hooks,n=window.wp.components,i=window.wp.data,s=window.wp.coreData,a=window.wp.i18n,r=window.wp.primitives,o=window.ReactJSXRuntime;var c=(0,o.jsx)(r.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",children:(0,o.jsx)(r.Path,{d:"m11.3 17.2-5-5c-.1-.1-.1-.3 0-.4l2.3-2.3-1.1-1-2.3 2.3c-.7.7-.7 1.8 0 2.5l5 5H7.5v1.5h5.3v-5.2h-1.5v2.6zm7.5-6.4-5-5h2.7V4.2h-5.2v5.2h1.5V6.8l5 5c.1.1.1.3 0 .4l-2.3 2.3 1.1 1.1 2.3-2.3c.6-.7.6-1.9-.1-2.5z"})}),l=(0,o.jsx)(r.SVG,{xmlns:"http://www.w3.org/2000/svg",viewBox:"0 0 24 24",children:(0,o.jsx)(r.Path,{d:"m13.06 12 6.47-6.47-1.06-1.06L12 10.94 5.53 4.47 4.47 5.53 10.94 12l-6.47 6.47 1.06 1.06L12 13.06l6.47 6.47 1.06-1.06L13.06 12Z"})});const u=window.wp.compose,d=window.wp.element,g=window.wp.editor,m=window.wp.notices,p=window.wp.url,h=window.wp.apiFetch;var w=t.n(h);let f=!1;const _=()=>{f||(console.warn("[AI] Client ability execution is unavailable. Falling back to REST."),f=!0)},y=(t,e,n)=>{if("GET"===n||"DELETE"===n)return{path:null==e?`/wp-abilities/v1/abilities/${t}/run`:(0,p.addQueryArgs)(`/wp-abilities/v1/abilities/${t}/run`,{input:e}),method:n};const i={path:`/wp-abilities/v1/abilities/${t}/run`,method:"POST"};return void 0!==e&&(i.data={input:e}),i};const b=window.wp.wordcount;const x="ai_content_classification_error",v={},C={},j=t=>{const e=Number.parseInt(String(t??5),10);return Number.isNaN(e)?5:Math.min(10,Math.max(1,e))},A=()=>{const t=window.aiContentClassificationData??{};return{enabled:t.enabled??!1,strategy:t.strategy??"existing_only",maxSuggestions:j(t.maxSuggestions),minContentLength:t.minContentLength??250}};function N(t){const{postId:e,content:n}=(0,i.useSelect)(t=>{const e=t(g.store);return{postId:e.getCurrentPostId(),content:e.getEditedPostContent()}},[]),[r,o]=(0,d.useState)(!1),[c,l]=(0,d.useState)(()=>v[t]??[]);(0,d.useEffect)(()=>{v[t]=c},[t,c]);const{removeNotice:u,createErrorNotice:h}=(0,i.dispatch)(m.store),{minContentLength:f}=A(),j=function(t,e){return function(t){return(0,b.count)(t,"characters_excluding_spaces")}(t)>=e}(n||"",f),N=(0,d.useCallback)(async()=>{if(!function(t){const e=window.aiProviderData??{hasProvider:!1,connectorsUrl:""};if(e.hasProvider)return!0;const{connectorsUrl:n}=e;return(0,i.dispatch)(m.store).createErrorNotice((0,a.__)("This feature requires an AI Connector to function properly.","ai"),{id:t,isDismissible:!0,actions:n?[{label:(0,a.__)("Manage Connectors","ai"),url:n}]:[]}),!1}(x))return;C[t]=(C[t]||0)+1;const n=A();o(!0),l([]),u(x);try{const a=(0,i.select)(g.store).getEditedPostContent(),r=await async function(t,e,n,i,s){const a={content:e,post_id:t,taxonomy:n,strategy:i,max_suggestions:s},r=await async function(t,e,n){try{const n=await(async()=>{try{const{ready:t}=await import("@wordpress/core-abilities");return await t,await import("@wordpress/abilities")}catch{return null}})();if("function"==typeof n?.executeAbility)return await n.executeAbility(t,e);_()}catch(t){if(console.error(t),!(t=>{if(!t||"object"!=typeof t)return!1;const e="message"in t&&"string"==typeof t.message?t.message:"",n="code"in t&&"string"==typeof t.code?t.code:"";return"ability_not_found"===n||e.includes("Ability not found")||e.includes("missing callback")||"ability_invalid_input"===n})(t))throw t;_()}const i=n?.method??"POST";return await w()({...y(t,e,i),...n?.signal?{signal:n.signal}:{}})}("ai/content-classification",a);return r?.suggestions&&Array.isArray(r.suggestions)?r.suggestions:[]}(e,a,t,n.strategy,n.maxSuggestions),o=await async function(t){const e=(0,i.select)(s.store).getTaxonomy(t),n=e?.rest_base??t,{getEditedPostAttribute:a}=(0,i.select)(g.store),r=a(n)??[];if(!r.length)return[];try{return(await w()({path:(0,p.addQueryArgs)(`/wp/v2/${n}`,{include:r.join(","),per_page:r.length})})).map(t=>t.name.toLowerCase())}catch{return[]}}(t),c=r.filter(t=>!o.includes(t.term.toLowerCase()));l(c)}catch(t){h(t?.message||t,{id:x,isDismissible:!0})}finally{o(!1)}},[e,t,u,h]),P=(0,d.useCallback)(e=>{let n=-1;l(t=>(n=t.findIndex(t=>t.term===e.term),t.filter(t=>t.term!==e.term)));const a=C[t]||0;(async function(t,e){const n=(0,i.select)(s.store).getTaxonomy(t),a=n?.rest_base??t;let r;if(e.parent&&n?.hierarchical){const n=await S(t,a,e.parent);if(!n)return!1;r=n}const{editPost:o}=(0,i.dispatch)(g.store),{getEditedPostAttribute:c}=(0,i.select)(g.store),l=c(a)??[],u=await S(t,a,e.term,r);return!!u&&(l.includes(u)||o({[a]:[...l,u]}),!0)})(t,e).then(i=>{i||(C[t]||0)===a&&l(t=>function(t,e,n){if(t.some(t=>t.term===e.term))return t;if(n<0||n>t.length)return[...t,e];const i=[...t];return i.splice(n,0,e),i}(t,e,n))})},[t]),D=(0,d.useCallback)(t=>{(t=>{l(e=>e.filter(e=>e.term!==t.term))})(t)},[]),E=(0,d.useCallback)(()=>{C[t]=(C[t]||0)+1,l([])},[t]);return{isGenerating:r,suggestions:c,hasEnoughContent:j,handleGenerate:N,handleAccept:P,handleDismiss:D,handleDismissAll:E,minContentLength:f}}async function S(t,e,n,a){try{const r=await w()({path:(0,p.addQueryArgs)(`/wp/v2/${e}`,{search:n,per_page:100})});if(Array.isArray(r)){const t=r.find(t=>t.name.toLowerCase()===n.toLowerCase());if(t)return t.id}const o={name:n};a&&(o.parent=a);const c=await(0,i.dispatch)(s.store).saveEntityRecord("taxonomy",t,o);return c?.id??null}catch(t){const{createErrorNotice:e}=(0,i.dispatch)(m.store);return e(t?.message||`Could not add term "${n}". Please try again.`,{id:`${x}_term_${n}`,isDismissible:!0}),null}}function P({taxonomy:t}){const{isGenerating:e,suggestions:r,hasEnoughContent:g,handleGenerate:m,handleAccept:p,handleDismiss:h,handleDismissAll:w,minContentLength:f}=N(t),_=(0,i.useSelect)(e=>e(s.store).getTaxonomy(t),[t]),y=(0,u.useInstanceId)(P,"suggestion-panel-classification-hint"),b=(0,u.useInstanceId)(P,"suggestion-panel-classification-suggestions"),x=(0,d.useRef)(!1),v=(0,d.useRef)(!1),C=_?.name??t,j=r.length>0,A=t=>{x.current&&t&&(t.focus(),x.current=!1)},S=t=>{v.current=t,x.current=!0,m()},D=t=>{x.current=r.length>1||t,v.current=1===r.length};return(0,o.jsxs)("div",{className:"ai-content-classification",children:[!j&&(0,o.jsx)(n.Button,{accessibleWhenDisabled:!0,icon:c,variant:"secondary",onClick:()=>S(!1),disabled:e||!g,isBusy:e,ref:t=>{v.current&&t&&(t.focus(),v.current=!1)},className:"ai-content-classification__generate-button",__next40pxDefaultSize:!0,"aria-describedby":g?void 0:y,children:e?(0,a.__)("Generating…","ai"):(0,a.sprintf)(/* translators: %s: Taxonomy label (e.g., "Tags" or "Categories"). */ /* translators: %s: Taxonomy label (e.g., "Tags" or "Categories"). */
2 (0,a.__)("Suggest %s","ai"),C)}),!g&&!j&&(0,o.jsx)("p",{id:y,className:"ai-content-classification__hint components-base-control__help",style:{color:"#757575"},children:(0,a.sprintf)(/* translators: %d: Minimum content length in characters. */ /* translators: %d: Minimum content length in characters. */
3 (0,a.__)("Content Classification will be available when the post content has at least %d characters.","ai"),f)}),j&&(0,o.jsxs)("div",{className:"ai-content-classification__suggestions",children:[(0,o.jsx)("h3",{id:b,className:"ai-content-classification__suggestions-title",children:(0,a.sprintf)(/* translators: %s: Taxonomy label (e.g., "Tags" or "Categories"). */ /* translators: %s: Taxonomy label (e.g., "Tags" or "Categories"). */
4 (0,a.__)("Suggested %s","ai"),C)}),(0,o.jsx)("div",{className:"ai-content-classification__pills","aria-labelledby":b,role:"list",children:r.map(t=>(0,o.jsxs)("span",{className:"ai-content-classification__pill"+(t.is_new?" ai-content-classification__pill--new":""),role:"listitem",children:[(0,o.jsxs)(n.Button,{className:"ai-content-classification__pill-accept",onClick:()=>{D(!0),p(t)},ref:t===r[0]?A:void 0,label:(0,a.sprintf)(/* translators: %s: Term name. */ /* translators: %s: Term name. */
5 (0,a.__)('Add "%s"',"ai"),t.term),size:"small",children:[t.parent&&(0,o.jsx)("span",{className:"ai-content-classification__pill-parent",children:t.parent+((0,a.isRTL)()?"":"")}),t.term,t.is_new&&(0,o.jsx)("span",{className:"ai-content-classification__pill-badge",children:(0,a.__)("new","ai")})]}),(0,o.jsx)(n.Button,{className:"ai-content-classification__pill-dismiss",icon:l,iconSize:16,onClick:()=>{D(!1),h(t)},label:(0,a.sprintf)(/* translators: %s: Term name. */ /* translators: %s: Term name. */
6 (0,a.__)('Dismiss "%s"',"ai"),t.term),size:"small"})]},t.term))}),(0,o.jsxs)(n.Flex,{gap:3,className:"ai-content-classification__actions",children:[(0,o.jsx)(n.FlexItem,{children:(0,o.jsx)(n.Button,{variant:"link",onClick:()=>S(!0),children:(0,a.__)("Suggest again","ai")})}),(0,o.jsx)(n.FlexItem,{children:(0,o.jsx)(n.Button,{variant:"link",onClick:()=>{x.current=!1,v.current=!0,w()},isDestructive:!0,children:(0,a.__)("Dismiss all","ai")})})]})]})]})}const D=["post_tag","category"];(0,e.addFilter)("editor.PostTaxonomyType","ai/content-classification",function(t){return function(e){const{slug:n}=e;return D.includes(n)?(0,o.jsxs)(o.Fragment,{children:[(0,o.jsx)(t,{...e}),(0,o.jsx)(P,{taxonomy:n})]}):(0,o.jsx)(t,{...e})}})})();