@@ -186,7 +186,7 @@ var Anchor = Anchor || (() => {
186186 ...Object . keys ( ALIAS_MAP ) ,
187187 'remove' , 'lock' , 'unlock' , 'center' , 'update' , 'info' ,
188188 'track' , 'untrack' , 'retrack' ,
189- 'chain' ,
189+ 'chain' , 'unchain' ,
190190 'ignore-selected' , 'persist' ,
191191 'config' ,
192192 '--help' ,
@@ -1136,6 +1136,9 @@ var Anchor = Anchor || (() => {
11361136 `<b>${ CMD_TOKEN } chain [component flags] [ignore-selected] [child_id...]</b>` ,
11371137 'Mutually anchor tokens in a ring (A\u2192B, B\u2192C, C\u2192A). Move any one, all follow.' ,
11381138 '' ,
1139+ `<b>${ CMD_TOKEN } unchain [ignore-selected] [child_id...]</b>` ,
1140+ 'Dissolve a chain ring. Select any one token in the ring.' ,
1141+ '' ,
11391142 `<b>${ CMD_TOKEN } update [ignore-selected] [child_id...]</b>` ,
11401143 'Force immediate transform sync.' ,
11411144 '' ,
@@ -1511,7 +1514,7 @@ var Anchor = Anchor || (() => {
15111514 // Only skip the first otherArg as a potential anchor ID when we're
15121515 // establishing a new anchor relationship AND it's actually a valid graphic.
15131516 // If there's no valid graphic as the first arg, all otherArgs are child IDs.
1514- const ACTION_FLAGS = [ 'remove' , 'lock' , 'unlock' , 'center' , 'update' , 'info' , 'track' , 'untrack' , 'retrack' , 'chain' ] ;
1517+ const ACTION_FLAGS = [ 'remove' , 'lock' , 'unlock' , 'center' , 'update' , 'info' , 'track' , 'untrack' , 'retrack' , 'chain' , 'unchain' ] ;
15151518 const hasAction = ACTION_FLAGS . some ( f => flags . has ( f ) ) ;
15161519 const isNewAnchor = ! hasAction && ( Object . keys ( FLAG_EXPANSIONS ) . some ( f => flags . has ( f ) ) || flags . size === 0 ) ;
15171520 const firstArgIsAnchor = isNewAnchor &&
@@ -1656,6 +1659,21 @@ var Anchor = Anchor || (() => {
16561659 }
16571660 }
16581661
1662+ // Unchain — dissolve a chain ring from any member
1663+ if ( flags . has ( 'unchain' ) ) {
1664+ const ids = resolveChildIds ( msg , flags , otherArgs ) ;
1665+ if ( ids . length === 0 ) {
1666+ reply ( msg , 'Error' , 'Select or specify a token in the chain.' ) ;
1667+ } else {
1668+ var unchained = unchainAnchorObjs ( ids [ 0 ] ) ;
1669+ if ( unchained ) {
1670+ reply ( msg , 'Info' , 'Unchained ' + unchained . length + ' tokens.' ) ;
1671+ } else {
1672+ reply ( msg , 'Error' , 'Token is not part of a chain ring.' ) ;
1673+ }
1674+ }
1675+ }
1676+
16591677 // Info
16601678 if ( flags . has ( 'info' ) ) {
16611679 if ( childIds . length > 0 ) {
@@ -1745,6 +1763,43 @@ var Anchor = Anchor || (() => {
17451763 }
17461764 } ;
17471765
1766+ /**
1767+ * Walk the anchor chain from a starting token and find the ring.
1768+ * Returns the array of IDs forming the ring, or null if no ring found.
1769+ * The starting token does not need to be in the ring itself — if it's
1770+ * a child of a ring member, the ring is still found.
1771+ */
1772+ const walkChain = ( startId ) => {
1773+ const s = state [ SCRIPT_NAME ] ;
1774+ const visited = [ ] ;
1775+ var current = startId ;
1776+ while ( true ) {
1777+ var info = s . anchorInfoByChildId [ current ] ;
1778+ if ( ! info ) return null ; // not a child — dead end, no ring
1779+ visited . push ( current ) ;
1780+ var nextId = info . anchor_id ;
1781+ var idx = visited . indexOf ( nextId ) ;
1782+ if ( idx !== - 1 ) return visited . slice ( idx ) ; // found the ring
1783+ current = nextId ;
1784+ if ( visited . length > 1000 ) return null ; // safety cap
1785+ }
1786+ } ;
1787+
1788+ /**
1789+ * Unchain a ring of anchored tokens. Given any token ID in the ring,
1790+ * walks the chain and removes all anchor relationships.
1791+ * Returns the array of unchained IDs, or null if the token is not in a ring.
1792+ */
1793+ const unchainAnchorObjs = ( startId ) => {
1794+ var ids = walkChain ( startId ) ;
1795+ if ( ! ids ) {
1796+ log ( SCRIPT_NAME + ': unchainAnchorObjs — token is not part of a chain ring.' ) ;
1797+ return null ;
1798+ }
1799+ ids . forEach ( function ( id ) { removeAnchor ( id ) ; } ) ;
1800+ return ids ;
1801+ } ;
1802+
17481803 /**
17491804 * Programmatically create an invisible auto-anchor token for `obj` and
17501805 * establish the anchor relationship immediately.
@@ -2450,6 +2505,7 @@ var Anchor = Anchor || (() => {
24502505 lock,
24512506 unlock,
24522507 chainAnchorObjs,
2508+ unchainAnchorObjs,
24532509 } ,
24542510 } ;
24552511} ) ( ) ;
0 commit comments