Attach any file, take shares from other apps, and survive a backwards highlight
Attachments were images only. Now any file can be attached: from the file chooser behind the "+" menu, or from Android's share sheet, which the app is now in. An image still goes to the model as a picture; anything else is stored under its own name (`<hex>-<name>`, cleaned by `safe_file_name`) and the Claude driver ends the message with `Attached file: /abs/path`, since the CLI reads files by path and a model cannot be shown a trace. The user-message field is renamed `images` -> `attachments` on both sides, with a serde alias reading the rows written before. A share arrives before anyone has said which session it is for, so it is held in AppRoot with a banner on the list until a session takes it; an open session takes it at once. Unreadable shares are reported beside the composer, not thrown. The tool card crashed the app when opened on a command holding a quoted glob such as `-path '*/.git/*'`: highlights 1.1.0's shell lexer answers `x '*/a/*'` with a span whose end is before its start, and AnnotatedString refuses the range. Such spans are dropped; the library is the place for the fix. The echo driver gains `/bash <command>` so a card with a given command can be produced on the emulator. ui-sandbox.sh's token salvage read the tokens block's close only at a line start, ran past the compact `),],` the server writes, and copied `setups` into the new config twice, which the server then refused. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
1 parent
4bc69e8f9c
commit
6180663f14
25 files changed
+672
-165
No files matched your search
@@ -0,0 +1,44 @@
|
||||
package com.example.aiapp
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.core.content.IntentCompat
|
||||
|
||||
/**
|
||||
* What another app handed this one through the share sheet, waiting to be attached to a session.
|
||||
*
|
||||
* Held as the URIs rather than uploaded on arrival, because an upload belongs to a session and the
|
||||
* share arrives before anyone has said which. [serial] makes two shares of the same thing two
|
||||
* requests, for the reason [SessionOpenRequest] carries one: equal values would not recompose.
|
||||
*/
|
||||
data class ShareRequest(val uris: List<Uri>, val text: String?, val serial: Int)
|
||||
|
||||
/** The share in [intent], or null when it is some other intent. */
|
||||
fun sharedContent(intent: Intent, serial: Int): ShareRequest? {
|
||||
val uris =
|
||||
when (intent.action) {
|
||||
Intent.ACTION_SEND ->
|
||||
listOfNotNull(
|
||||
IntentCompat.getParcelableExtra(intent, Intent.EXTRA_STREAM, Uri::class.java)
|
||||
)
|
||||
Intent.ACTION_SEND_MULTIPLE ->
|
||||
IntentCompat.getParcelableArrayListExtra(
|
||||
intent,
|
||||
Intent.EXTRA_STREAM,
|
||||
Uri::class.java,
|
||||
)
|
||||
.orEmpty()
|
||||
else -> return null
|
||||
}
|
||||
val text = intent.getStringExtra(Intent.EXTRA_TEXT)?.takeIf { it.isNotBlank() }
|
||||
if (uris.isEmpty() && text == null) return null
|
||||
return ShareRequest(uris, text, serial)
|
||||
}
|
||||
|
||||
/** What is waiting, for the banner that says so. */
|
||||
fun ShareRequest.summary(): String =
|
||||
when {
|
||||
uris.size == 1 -> "1 file to attach"
|
||||
uris.isNotEmpty() -> "${uris.size} files to attach"
|
||||
else -> "Text to attach"
|
||||
}
|
||||
Reference in new issue
Block a user