How copy the shadow of an overwritten shadow tid?

I have an overwritten shadow tiddler, say $:/core/images/italic

How can the full original shadow tiddler be copied, without touching the overwrite (such as by e.g temporarily renaming the overwrite so that the shadow “kicks in”, copy it, then rename the overwrite back again)?

I expected the following to work (easily tested here if you if you first overwrite the linked “italic” tiddler and paste the below code into a new tiddler) - but it doesn’t work: It creates a copy of the overwrite, not of the shadow. It seems the elaborate filter shuffling still just transcludes the overwrite, but so how else can the shadow be reached?

\define testCreate()
<$action-createtiddler
  $basetitle="ShadowCopy"
  $template={{{ [[$:/core/images/italic]shadowsource[]plugintiddlers[]title[$:/core/images/italic]] }}}
/>
\end

<$button actions=<<testCreate>>> Create ShadowCopy </$button>

[[$:/core/images/italic]] [[ShadowCopy]]

BTW, this question is related to How to Compare shadows tiddlers content with tiddler!

I believe you can possibly do this but you will have to iterate all fields and “copy each”, except one the title. However before tackling this. it is possible to export the tiddler, then delete it, and reimport the tiddler once done.

  • Similarly rename the tiddler, the shadow then becomes visible. Get the content then rename it back.

Thanks, yeah… I’m naively hoping for some smoother solution.

Yeah, but as mentioned, this is not desirable. And your export idea is also not usable. My use case is plugin that should feature this “copying of the underlying shadow tiddler”, and I’m hoping to be able to say “Don’t worry, your overwrite is not touched when copying the shadow”.

Eg if you overwrite $:/Acknowledgements from the $:/core plugin you can use the following code to get the shadow info back.

<$set name="text" tiddler="$:/core" subtiddler="$:/Acknowledgements">

<<text>>

</$set>

@pmario - does that not only get the text field info? I hope to copy the full shadow (but modified title and tags) into a new tid.

Hi @twMat this is an area where I we’re now well placed to make some improvements.

The new JSON operators provide a means to extract the raw JSON of a shadow tiddler from a plugin. For example, try this filter in the “$:/AdvancedSearch” filter tab:

[{$:/core}jsonextract[tiddlers],[$:/core/copyright.txt]]

What I think we also need is a way to create a tiddler from such a blob of JSON; perhaps a new $json attribute for the <$action-setfield> widget so that there would be an easy way to allow selected fields to be overwritten. It may also be useful for <$action-createtiddler> to support the $json attribute.

1 Like

Thanks @jeremyruston , while I’m concluding my OP can’t quite be solved yet, I tink that this:

…would indeed be extremely useful, especially considering 3’d party integrations.

Try the following code with TW v5.3.0-preview

\function newTitle() "new" [<shadow>] +[join[-]]
\function getShadowsource() [<shadow>shadowsource[]]

\procedure cloneShadow()
<$set name=fields filter="[<shadow>subtiddlerfields<getShadowsource>] -[[title]]" >
	<$list filter="[enlist<fields>]" variable=fieldName>
		<$set name="content" field=<<fieldName>> tiddler=<<getShadowsource>> subtiddler=<<shadow>> >
			<!-- <$action-log newTitle=<<newTitle>> getShadowSource=<<getShadowsource>> fieldName=<<fieldName>> /> -->
			<$action-setfield $tiddler=<<newTitle>> $field=<<fieldName>> $value=<<content>> />
		</$set>
	</$list>
</$set>
\end

<$let shadow="$:/core/copyright.txt">
This button will create a new-$:/core/copyright.txt tiddler

<$button actions=<<cloneShadow>> >Clone me a river</$button>
</$let>

Docs for the operators can be found at: https://tiddlywiki.com/#Filter%20Operators

clone-a-shadow-tiddler-from-shadowsource.json (941 Bytes)

2 Likes

@pmario - a warm thank you! You went the ambitious subtiddlerfields[] way that I was a bit nervous about but you show it works perfectly!

I haven’t quite converted to 5.3.0 yet, so I’m trying to modify your code for 5.2.7, but I can’t get one of the parts working, as indicated with ``???` - any ideas? I’m pretty sure there must be a filter prefix that works.

\define newTitle() new-$(shadow)$

\define cloneShadow()
<$set name=fields filter="[<shadow>shadowsource[]] :??????[<shadow>subtiddlerfields<currentTiddler>] -[[title]]" >
	<$list filter="[enlist<fields>]" variable=fieldName>
		<$set name="content" field=<<fieldName>> tiddler={{{ [<shadow>shadowsource[]] }}} subtiddler=<<shadow>> >
			<$action-setfield $tiddler=<<newTitle>> $field=<<fieldName>> $value=<<content>> />
		</$set>
	</$list>
</$set>
\end

<$let shadow="$:/core/copyright.txt">
This button will create a new-$:/core/copyright.txt tiddler

<$button actions=<<cloneShadow>> >Clone me a river</$button>
</$let>

The following works (again thanks @pmario !) but, strangely, that outer $set cannot be merged into the $let widget!? Does anyone understand why? I do know the syntax (as seen in the other $let params I’m using) so I can’t help but think there’s some core bug…?

\define cloneShadow()
<$let 
	curr='$(currentTiddler)$'
	shadowsource={{{ [<curr>shadowsource[]] }}} >
<$set name=fields filter='[<curr>subtiddlerfields<shadowsource>] -[[title]]' >
	<$list filter="[enlist<fields>]" variable=fieldName>
		<$set name=content
			field=<<fieldName>> 
			tiddler={{{ [<curr>shadowsource[]] }}}
			subtiddler=<<curr>> >
			<$action-setfield $tiddler=foobar $field=<<fieldName>> $value=<<content>> />
		</$set>
	</$list>
</$set>
</$let>
\end

The $let widget only supports the simple <$set name=x value=y> syntax. The code here is using the filter attribute of the set widget which evaluates a filter and assigns the full title list to the variable.

Thanks, but so what is the difference between #2 and #3 in the following? Does #2 not “assign a full title list to the variable” (albeit only with a single title in the list)?

<$let 
	curr='$(currentTiddler)$'
	shadowsource={{{ [<curr>shadowsource[]] }}} >
	fields={{{ [<curr>subtiddlerfields<shadowsource>] -[[title]] }}}
 >

You could use

<$list filter='[<curr>subtiddlerfields<shadowsource>] -[[title]]' variable=fieldName>

and </$list> instead of

<$set name=fields filter='[<curr>subtiddlerfields<shadowsource>] -[[title]]' >
	<$list filter="[enlist<fields>]" variable=fieldName>

and

</$list>
</$set>

OH! - and DOH! Why did I not think of that! :joy:
Thanks.

As you note, #2 only assigns the first result of the filter to the variable. The use of the set widget with a filter attribute yields the entire result list, formatted with double square brackets if needed. I think you may be able to replicate its action with something like this:

<$let 
	curr='$(currentTiddler)$'
	shadowsource={{{ [<curr>shadowsource[]] }}} >
	fields={{{ [<curr>subtiddlerfields<shadowsource>] -[[title]] +[format:titlelist[]] }}}
 >

Thank you, that makes sense :grinning:

  • Could we not have a way to override this if desired?
  • There are cases where we want to pass a list to an attribute value.
  • It is a kind of invisible action (use 1st result only) which we could replace with a visible one like +first[]] or limit[1]
  • It effectively limits the capabilities of the filtered transclusion.

Attributes are defined as single strings, not lists; that is done for compatibility with HTML. I think the use cases for assigning a list to an attribute are covered by +[format:titlelist[]].

If course an attribute / list need not be just titles such as a list of classes, a list of numbers etc… But I suppose we can use +[join[ ]] to make it a single value and we should perhaps document it along side the documentation that only the first value is used when filtered transclusions are used as attributes.

  • with macros procedures and custom widgets it makes it harder to pass a list in a parameter.